diff options
author | John Keiser <john@johnkeiser.com> | 2016-01-15 15:07:19 -0800 |
---|---|---|
committer | John Keiser <john@johnkeiser.com> | 2016-01-15 15:07:19 -0800 |
commit | 12a4a60edefb91a74179ed2e8e58e73018ae9a69 (patch) | |
tree | df9149da15bca278a1b7a511643a5c105c2d3285 | |
parent | 89d6d7dedcc29bb68341c46f917bc522c24b2539 (diff) | |
parent | f7d83a4d895d5775245e80f4ad857ea92cfc5e02 (diff) | |
download | chef-12a4a60edefb91a74179ed2e8e58e73018ae9a69.tar.gz |
Merge branch 'jk/policies-local-mode'
3 files changed, 194 insertions, 41 deletions
diff --git a/lib/chef/chef_fs/chef_fs_data_store.rb b/lib/chef/chef_fs/chef_fs_data_store.rb index 99a668d50d..6bb884c59a 100644 --- a/lib/chef/chef_fs/chef_fs_data_store.rb +++ b/lib/chef/chef_fs/chef_fs_data_store.rb @@ -153,9 +153,9 @@ class Chef if use_memory_store?(path) @memory_store.create_dir(path, name, *options) else - with_dir(path) do |parent| + with_parent_dir(path + [name], *options) do |parent, name| begin - parent.create_child(chef_fs_filename(path + [name]), nil) + parent.create_child(name, nil) rescue Chef::ChefFS::FileSystem::AlreadyExistsError => e raise ChefZero::DataStore::DataAlreadyExistsError.new(to_zero_path(e.entry), e) end @@ -188,10 +188,23 @@ class Chef elsif path[0] == "cookbooks" && path.length == 2 # Do nothing. The entry gets created when the cookbook is created. + # /policy_groups/GROUP/policies/NAME + elsif path[0] == "policy_groups" && path[2] == "policies" + # Just set or create the proper entry in the hash + update_json(to_chef_fs_path(path[0..1]), {}, *options) do |group| + if policies.has_key?(path[3]) + raise ChefZero::DataStore::DataAlreadyExistsError.new(path, group) + end + + group["policies"] ||= {} + group["policies"][path[3]] = { "revision_id" => Chef::JSONCompat.parse(data) } + group + end + # create [/organizations/ORG]/users/NAME (with content '{}') # Manipulate the `members.json` file that contains a list of all users elsif is_org? && path == [ "users" ] - update_json("members.json", []) do |members| + update_json("members.json", [], *options) do |members| # Format of each entry: { "user": { "username": "jkeiser" } } if members.any? { |member| member["user"]["username"] == name } raise ChefZero::DataStore::DataAlreadyExistsError.new(path, entry) @@ -204,7 +217,7 @@ class Chef # create [/organizations/ORG]/association_requests/NAME (with content '{}') # Manipulate the `invitations.json` file that contains a list of all users elsif is_org? && path == [ "association_requests" ] - update_json("invitations.json", []) do |invitations| + update_json("invitations.json", [], *options) do |invitations| # Format of each entry: { "id" => "jkeiser-chef", 'username' => 'jkeiser' } if invitations.any? { |member| member["username"] == name } raise ChefZero::DataStore::DataAlreadyExistsError.new(path) @@ -219,9 +232,9 @@ class Chef raise "set only works with strings" end - with_dir(path) do |parent| + with_parent_dir(path + [name], *options) do |parent, name| begin - parent.create_child(chef_fs_filename(path + [name]), data) + parent.create_child(name, data) rescue Chef::ChefFS::FileSystem::AlreadyExistsError => e raise ChefZero::DataStore::DataAlreadyExistsError.new(to_zero_path(e.entry), e) end @@ -243,19 +256,18 @@ class Chef # /policy_groups/NAME/policies/POLICYNAME: return the revision of the given policy elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 4 - with_entry(path[0..1]) do |entry| - policy_group = Chef::JSONCompat.parse(entry) - if !policy_group["policies"] || !policy_group["policies"][path[3]] || !policy_group["policies"][path[3]] - raise ChefZero::DataStore::DataNotFoundError.new(path, entry) - end - # The policy group looks like: - # { - # "policies": { - # "x": { "revision_id": "10" } - # } - # } - Chef::JSONCompat.to_json_pretty(policy_group["policies"][path[3]]["revision_id"]) + # Just set or create the proper entry in the hash + policy_group = get_json(to_chef_fs_path(path[0..1]), {}) + if !policy_group["policies"] || !policy_group["policies"][path[3]] + raise ChefZero::DataStore::DataNotFoundError.new(path, entry) end + # The policy group looks like: + # { + # "policies": { + # "x": { "revision_id": "10" } + # } + # } + Chef::JSONCompat.to_json_pretty(policy_group["policies"][path[3]]["revision_id"]) # GET [/organizations/ORG]/users/NAME -> /users/NAME # Manipulates members.json @@ -326,13 +338,23 @@ class Chef # Write out the files! if path[0] == "cookbooks" && path.length == 3 write_cookbook(path, data, *options) + + # Handle /policy_groups/some_policy_group/policies/some_policy_name + elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 4 + # Just set or create the proper entry in the hash + update_json(to_chef_fs_path(path[0..1]), {}, *options) do |group| + group["policies"] ||= {} + group["policies"][path[3]] = { "revision_id" => Chef::JSONCompat.parse(data) } + group + end + else - with_dir(path[0..-2]) do |parent| - child = parent.child(chef_fs_filename(path)) + with_parent_dir(path, *options) do |parent, name| + child = parent.child(name) if child.exists? child.write(data) else - parent.create_child(chef_fs_filename(path), data) + parent.create_child(name, data) end end end @@ -343,6 +365,16 @@ class Chef if use_memory_store?(path) @memory_store.delete(path) + # DELETE /policy_groups/GROUP/policies/POLICY + elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 4 + update_json(to_chef_fs_path(path[0..1]), {}) do |group| + unless group["policies"] && group["policies"].has_key?(path[3]) + raise ChefZero::DataStore::DataNotFoundError.new(path) + end + group["policies"].delete(path[3]) + group + end + # DELETE [/organizations/ORG]/users/NAME # Manipulates members.json elsif is_org? && path[0] == "users" && path.length == 2 @@ -383,6 +415,24 @@ class Chef def delete_dir(path, *options) if use_memory_store?(path) @memory_store.delete_dir(path, *options) + + # DELETE /policies/POLICY + elsif path[0] == "policies" && path.length == 2 + with_entry(path[0..0]) do |policies| + # /policies: + # - a-1.0.0.json + # - a-1.0.1.json + # - b-2.0.0.json + found_policy = false + policies.children.each do |policy| + # We want to delete just the ones that == POLICY + next unless policy.name.rpartition('-')[0] == path[1] + policy.delete(false) + found_policy = true + end + raise ChefZero::DataStore::DataNotFoundError.new(path) if !found_policy + end + else with_entry(path) do |entry| begin @@ -398,15 +448,37 @@ class Chef if use_memory_store?(path) @memory_store.list(path) + # LIST /policies + elsif path == [ "policies" ] + with_entry([ path[0] ]) do |policies| + policies.children.map { |policy| policy.name[0..-6].rpartition('-')[0] }.uniq + end + + # LIST /policies/POLICY/revisions + elsif path[0] == "policies" && path[2] == "revisions" && path.length == 3 + with_entry([ path[0] ]) do |policies| + # /policies: + # - a-1.0.0.json + # - a-1.0.1.json + # - b-2.0.0.json + revisions = [] + policies.children.each do |policy| + name, dash, revision = policy.name[0..-6].rpartition('-') + revisions << revision if name == path[1] + end + raise ChefZero::DataStore::DataNotFoundError.new(path) if revisions.empty? + revisions + end + elsif path[0] == "policy_groups" && path.length == 2 with_entry(path) do |entry| [ "policies" ] end elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 3 - with_entry([ "policy_groups", path[1] ]) do |entry| - policy_group = Chef::JSONCompat.parse(entry.read) - (policy_group["policies"] || {}).keys + with_entry(path[0..1]) do |entry| + policies = Chef::JSONCompat.parse(entry.read)["policies"] || {} + policies.keys end elsif path[0] == "cookbooks" && path.length == 1 @@ -468,6 +540,12 @@ class Chef def exists?(path) if use_memory_store?(path) @memory_store.exists?(path) + + # /policy_groups/NAME/policies/POLICYNAME + elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 4 + group = get_json(to_chef_fs_path(path[0..1]), {}) + group["policies"] && group["policies"].has_key?(path[3]) + else path_always_exists?(path) || Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists? end @@ -476,14 +554,18 @@ class Chef def exists_dir?(path) if use_memory_store?(path) @memory_store.exists_dir?(path) + elsif path[0] == "cookbooks" && path.length == 2 list([ path[0] ]).include?(path[1]) + + # /policies/NAME + elsif path[0] == "policies" && path.length == 2 + list([ path[0] ]).include?(path[1]) + # /policy_groups/NAME/policies elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 3 exists_dir?(path[0..1]) - # /policy_groups/NAME/policies/POLICYNAME - elsif path[0] == "policy_groups" && path[2] == "policies" && path.length == 4 - exists_dir?(path[0..1]) && list(path[0..2]).include?(path[3]) + else Chef::ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists? end @@ -544,17 +626,20 @@ class Chef end def _to_chef_fs_path(path) + # /data -> /data_bags + # /data/BAG -> /data_bags/BAG + # /data/BAG/ITEM -> /data_bags/BAG/ITEM.json if path[0] == "data" path = path.dup path[0] = "data_bags" if path.length >= 3 path[2] = "#{path[2]}.json" end - elsif path[0] == "policies" - path = path.dup - if path.length >= 3 - path[2] = "#{path[2]}.json" - end + + # /policies/POLICY/revisions/REVISION -> /policies/POLICY-REVISION.json + elsif path[0] == "policies" && path[2] == "revisions" && path.length >= 4 + path = [ "policies", "#{path[1]}-#{path[3]}.json" ] + elsif path[0] == "cookbooks" if path.length == 2 raise ChefZero::DataStore::DataNotFoundError.new(path) @@ -618,6 +703,13 @@ class Chef end end + # /policies/NAME-REVISION.json -> /policies/NAME/revisions/REVISION + elsif path[0] == "policies" + if path.length >= 2 + name, dash, revision = path[1][0..-6].rpartition('-') + path = [ "policies", name, "revisions", revision ] + end + elsif path.length == 2 && path[0] != "cookbooks" path = path.dup path[1] = path[1][0..-6] @@ -641,6 +733,17 @@ class Chef end end + def with_parent_dir(path, *options) + path = _to_chef_fs_path(path) + begin + yield get_dir(path[0..-2], options.include?(:create_dir)), path[-1] + rescue Chef::ChefFS::FileSystem::NotFoundError => e + err = ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e) + err.set_backtrace(e.backtrace) + raise err + end + end + def with_dir(path) # Do not automatically create data bags create = !(path[0] == "data" && path.size >= 2) @@ -658,7 +761,7 @@ class Chef result = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path.join("/")) if result.exists? result - elsif create + elsif create || path.size == 1 get_dir(path[0..-2], create).create_child(result.name, nil) else raise ChefZero::DataStore::DataNotFoundError.new(path) @@ -671,16 +774,27 @@ class Chef metadata[:version] || "0.0.0" end - def update_json(path, default_value) + def update_json(path, default_value, *options) entry = Chef::ChefFS::FileSystem.resolve_path(chef_fs, path) begin input = Chef::JSONCompat.parse(entry.read) - output = yield input.dup - entry.write(Chef::JSONCompat.to_json_pretty(output)) if output != input + output = yield input + entry.write(Chef::JSONCompat.to_json_pretty(output)) if output != Chef::JSONCompat.parse(entry.read) rescue Chef::ChefFS::FileSystem::NotFoundError # Send the default value to the caller, and create the entry if the caller updates it output = yield default_value - entry.parent.create_child(entry.name, Chef::JSONCompat.to_json_pretty(output)) if output != [] + parent = entry.parent + parent = ensure_dir(parent) if options.include?(:create_dir) + parent.create_child(entry.name, Chef::JSONCompat.to_json_pretty(output)) if output != [] + end + end + + def ensure_dir(entry) + return entry if entry.exists? + parent = entry.parent + if parent + ensure_dir(parent) + parent.create_child(entry.name) end end diff --git a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_policies_dir.rb b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_policies_dir.rb new file mode 100644 index 0000000000..a4ad7c11c0 --- /dev/null +++ b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_policies_dir.rb @@ -0,0 +1,38 @@ +# +# Author:: John Keiser (<jkeiser@opscode.com>) +# Copyright:: Copyright (c) 2013 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/repository/chef_repository_file_system_entry" +require "chef/chef_fs/data_handler/policy_data_handler" + +class Chef + module ChefFS + module FileSystem + module Repository + class ChefRepositoryFileSystemPoliciesDir < ChefRepositoryFileSystemEntry + def initialize(name, parent, path = nil) + super(name, parent, path, Chef::ChefFS::DataHandler::PolicyDataHandler.new) + end + + def can_have_child?(name, is_dir) + !is_dir && name.include?("-") + end + end + end + end + end +end diff --git a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir.rb b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir.rb index 2add4072ea..8d9f2c21ad 100644 --- a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir.rb @@ -17,11 +17,12 @@ # require "chef/chef_fs/file_system/base_fs_dir" -require "chef/chef_fs/file_system/repository/chef_repository_file_system_entry" require "chef/chef_fs/file_system/repository/chef_repository_file_system_acls_dir" require "chef/chef_fs/file_system/repository/chef_repository_file_system_cookbooks_dir" -require "chef/chef_fs/file_system/repository/chef_repository_file_system_versioned_cookbooks_dir" require "chef/chef_fs/file_system/repository/chef_repository_file_system_data_bags_dir" +require "chef/chef_fs/file_system/repository/chef_repository_file_system_entry" +require "chef/chef_fs/file_system/repository/chef_repository_file_system_policies_dir" +require "chef/chef_fs/file_system/repository/chef_repository_file_system_versioned_cookbooks_dir" require "chef/chef_fs/file_system/multiplexed_dir" require "chef/chef_fs/data_handler/client_data_handler" require "chef/chef_fs/data_handler/environment_data_handler" @@ -170,6 +171,8 @@ class Chef end when 'cookbook_artifacts' dirs = paths.map { |path| ChefRepositoryFileSystemVersionedCookbooksDir.new(name, self, path) } + when "policies" + dirs = paths.map { |path| ChefRepositoryFileSystemPoliciesDir.new(name, self, path) } when "data_bags" dirs = paths.map { |path| ChefRepositoryFileSystemDataBagsDir.new(name, self, path) } when "acls" @@ -182,8 +185,6 @@ class Chef Chef::ChefFS::DataHandler::EnvironmentDataHandler.new when "nodes" Chef::ChefFS::DataHandler::NodeDataHandler.new - when "policies" - Chef::ChefFS::DataHandler::PolicyDataHandler.new when "policy_groups" Chef::ChefFS::DataHandler::PolicyGroupDataHandler.new when "roles" |