summaryrefslogtreecommitdiff
path: root/lib/chef/chef_fs/file_system/policy_group_entry.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef/chef_fs/file_system/policy_group_entry.rb')
-rw-r--r--lib/chef/chef_fs/file_system/policy_group_entry.rb135
1 files changed, 135 insertions, 0 deletions
diff --git a/lib/chef/chef_fs/file_system/policy_group_entry.rb b/lib/chef/chef_fs/file_system/policy_group_entry.rb
new file mode 100644
index 0000000000..9885310cc7
--- /dev/null
+++ b/lib/chef/chef_fs/file_system/policy_group_entry.rb
@@ -0,0 +1,135 @@
+#
+# Author:: John Keiser (<jkeiser@opscode.com>)
+# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/chef_fs/file_system/already_exists_error'
+require 'chef/chef_fs/file_system/not_found_error'
+require 'chef/chef_fs/file_system/operation_failed_error'
+
+class Chef
+ module ChefFS
+ module FileSystem
+ # Represents an entire policy group.
+ # Server path: /organizations/ORG/policy_groups/GROUP
+ # Repository path: policy_groups\GROUP.json
+ # Format:
+ # {
+ # "policies": {
+ # "a": { "revision_id": "1.0.0" }
+ # }
+ # }
+ class PolicyGroupEntry < RestListEntry
+ # delete is handled normally:
+ # DELETE /organizations/ORG/policy_groups/GROUP
+
+ # read is handled normally:
+ # GET /organizations/ORG/policy_groups/GROUP
+
+ # write is different.
+ # For each policy:
+ # - PUT /organizations/ORG/policy_groups/GROUP/policies/POLICY
+ # For each policy on the server but not the client:
+ # - DELETE /organizations/ORG/policy_groups/GROUP/policies/POLICY
+ # If the server has associations for a, b and c,
+ # And the client wants associations for a, x and y,
+ # We must PUT a, x and y
+ # And DELETE b and c
+ def write(file_contents)
+ # Parse the contents to ensure they are valid JSON
+ begin
+ object = Chef::JSONCompat.parse(file_contents)
+ rescue Chef::Exceptions::JSON::ParseError => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e, "Parse error reading JSON creating child '#{name}': #{e}")
+ end
+
+ if data_handler
+ object = data_handler.normalize_for_put(object, self)
+ data_handler.verify_integrity(object, self) do |error|
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, nil, "Error creating '#{name}': #{error}")
+ end
+ end
+
+ begin
+
+ # this should all get carted to PolicyGroupEntry#write.
+
+ # the server demands the full policy data, but we want users' local policy_group documents to just
+ # have the data you'd get from GET /policy_groups/POLICY_GROUP. so we try to fetch that.
+
+ # ordinarily this would be POST to the normal URL, but we do PUT to
+ # /organizations/{organization}/policy_groups/{policy_group}/policies/{policy_name} with the full
+ # policy data, for each individual policy.
+ policy_datas = {}
+
+ object["policies"].each do |policy_name, policy_data|
+ policy_path = "/policies/#{policy_name}/revisions/#{policy_data["revision_id"]}"
+
+ get_data = begin
+ rest.get(policy_path)
+ rescue Net::HTTPServerException => e
+ raise "Could not find policy '#{policy_name}'' with revision '#{policy_data["revision_id"]}'' on the server"
+ end
+
+ # GET policy data
+ server_policy_data = Chef::JSONCompat.parse(get_data)
+
+ # if it comes back 404, raise an Exception with "Policy file X does not exist with revision Y on the server"
+
+ # otherwise, add it to the list of policyfile datas.
+ policy_datas[policy_name] = server_policy_data
+ end
+
+ begin
+ existing_group = Chef::JSONCompat.parse(self.read)
+ rescue NotFoundError
+ # It's OK if the group doesn't already exist, just means no existing policies
+ end
+
+ # now we have the fullpolicy data for each policies, which is what the PUT endpoint demands.
+ policy_datas.each do |policy_name, policy_data|
+ # PUT /organizations/ORG/policy_groups/GROUP/policies/NAME
+ rest.put("#{api_path}/policies/#{policy_name}", policy_data)
+ end
+
+ # Now we need to remove any policies that are *not* in our current group.
+ if existing_group && existing_group['policies']
+ (existing_group['policies'].keys - policy_datas.keys).each do |policy_name|
+ rest.delete("#{api_path}/policies/#{policy_name}")
+ end
+ end
+
+ rescue Timeout::Error => e
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e, "Timeout creating '#{name}': #{e}")
+ rescue Net::HTTPServerException => e
+ # 404 = NotFoundError
+ if e.response.code == "404"
+ raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
+ # 409 = AlreadyExistsError
+ elsif $!.response.code == "409"
+ raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self, e, "Failure creating '#{name}': #{path}/#{name} already exists")
+ # Anything else is unexpected (OperationFailedError)
+ else
+ raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e, "Failure creating '#{name}': #{e.message}")
+ end
+ end
+
+ self
+ end
+ end
+ end
+ end
+end