diff options
author | John Keiser <jkeiser@opscode.com> | 2014-07-16 07:31:34 -0700 |
---|---|---|
committer | John Keiser <jkeiser@opscode.com> | 2014-07-23 19:21:23 -0600 |
commit | e811e7aa8ffce23430453aa389da5c38f4ee087d (patch) | |
tree | ace417786593e4517651a0d9cc02a949b75cfc8d /lib | |
parent | 856e52dd7abfd78259721528254a5b30e13d9cd3 (diff) | |
download | chef-zero-e811e7aa8ffce23430453aa389da5c38f4ee087d.tar.gz |
Move org defaults to a common class, DefaultFacade
for readability and so that memory and disk stores can share the defaults
and so that they can be switched out at will
Diffstat (limited to 'lib')
-rw-r--r-- | lib/chef_zero/data_normalizer.rb | 46 | ||||
-rw-r--r-- | lib/chef_zero/data_store/default_facade.rb | 316 | ||||
-rw-r--r-- | lib/chef_zero/data_store/interface_v1.rb | 13 | ||||
-rw-r--r-- | lib/chef_zero/data_store/memory_store_v2.rb | 61 | ||||
-rw-r--r-- | lib/chef_zero/data_store/v1_to_v2_adapter.rb | 99 | ||||
-rw-r--r-- | lib/chef_zero/server.rb | 4 |
6 files changed, 345 insertions, 194 deletions
diff --git a/lib/chef_zero/data_normalizer.rb b/lib/chef_zero/data_normalizer.rb index 30a133a..59c4335 100644 --- a/lib/chef_zero/data_normalizer.rb +++ b/lib/chef_zero/data_normalizer.rb @@ -4,19 +4,13 @@ require 'chef_zero/rest_base' module ChefZero class DataNormalizer def self.normalize_acls(acls, path, container_acls) - defaults = {} + acls = container_acls.merge(acls) %w(create read update delete grant).each do |perm| acls[perm] ||= {} - default_perm = { 'actors' => [], 'groups' => [ 'admins' ] } - default_perm.merge!(container_acls[perm]) if container_acls[perm] - default_perm.merge!(get_org_default_acls(path, perm)) # Bring in what Chef Server would have filled in on initial org setup - if acls[perm] - acls[perm] = default_perm.merge(acls[perm]) - else - acls[perm] = default_perm - end + acls[perm]['actors'] ||= [] + acls[perm]['groups'] ||= [ 'admins' ] end - defaults.merge(acls) + acls end def self.normalize_client(client, name) @@ -181,38 +175,6 @@ module ChefZero private - DEFAULT_ACL_GROUPS = { - # https://github.com/opscode/mixlib-authorization/blob/master/lib/mixlib/authorization/default_organization_policy.rb - 'containers' => { - %w(cookbooks roles environments) => { - %w(create update delete) => %w(admins users), - %w(read) => %w(admins users clients), - }, - %w(data) => { - %w(create read update delete) => %w(admins users clients), - }, - %w(nodes) => { - %w(create read) => %w(admins users clients), - %w(update delete) => %w(admins users), - }, - %w(clients) => { - %w(read delete) => %w(admins users), - }, - %w(groups containers) => { - %w(read) => %w(admins users), - }, - %w(sandboxes) => { - %w(create) => %w(admins users) - } - }, - 'groups' => { - %w(billing-admins) => { - %w(read update) => %w(billing-admins), - %w(grant create delete) => [], - } - } - } - def self.get_org_default_acls(path, perm) name_lists = DEFAULT_ACL_GROUPS[path[2]] if name_lists diff --git a/lib/chef_zero/data_store/default_facade.rb b/lib/chef_zero/data_store/default_facade.rb new file mode 100644 index 0000000..4ab775f --- /dev/null +++ b/lib/chef_zero/data_store/default_facade.rb @@ -0,0 +1,316 @@ +require 'chef_zero/data_store/interface_v2' + +module ChefZero + module DataStore + class DefaultFacade < ChefZero::DataStore::InterfaceV2 + def initialize(real_store) + @real_store = real_store + clear + end + + attr_reader :real_store + + def default(path, name=nil) + value = @defaults + for part in path + break if !value + value = value[part] + end + value = value[name] if value && name + if value.is_a?(Proc) + return value.call(self, path) + else + return value + end + end + + def delete_default(path) + value = @defaults + for part in path[0..-2] + break if !value + value = value[part] + end + if value + !!value.delete(path[-1]) + else + false + end + end + + def clear + real_store.clear if real_store.respond_to?(:clear) + @defaults = { + 'organizations' => {} + } + end + + def create_dir(path, name, *options) + if default(path, name) && !options.include?(:recursive) + raise DataAlreadyExistsError.new(path + [name]) + end + begin + real_store.create_dir(path, name, *options) + rescue DataNotFoundError + if default(path) + real_store.create_dir(path, name, :recursive, *options) + else + raise + end + end + + # If the org hasn't been created, create its defaults + if path.size > 0 && path[0] == 'organizations' + if path.size == 1 + @defaults['organizations'][name] ||= org_defaults(name) + else + @defaults['organizations'][path[1]] ||= org_default(path[1]) + end + end + end + + def create(path, name, data, *options) + if default(path, name) && !options.include?(:create_dir) + raise DataAlreadyExistsError.new(path + [name]) + end + begin + real_store.create(path, name, data, *options) + rescue DataNotFoundError + if default(path) + real_store.create(path, name, data, :create_dir, *options) + else + raise + end + end + # If the org hasn't been created, create its defaults + if path.size > 0 && path[0] == 'organizations' + if path.size == 1 + @defaults['organizations'][name] ||= org_defaults(name) + else + @defaults['organizations'][path[1]] ||= org_defaults(path[1]) + end + end + end + + def get(path, request=nil) + begin + real_store.get(path, request=nil) + rescue DataNotFoundError + result = default(path) + if result + result + else + raise + end + end + end + + def set(path, data, *options) + begin + real_store.set(path, data, *options) + rescue DataNotFoundError + if default(path) + real_store.create(path[0..-2], path[-1], data, *options) + else + raise + end + end + end + + def delete(path) + deleted = delete_default(path) + begin + real_store.delete(path) + rescue DataNotFoundError + if deleted + return + else + raise + end + end + end + + def delete_dir(path, *options) + deleted = delete_default(path) + begin + real_store.delete_dir(path, *options) + rescue DataNotFoundError + if !deleted + raise + end + end + end + + def list(path) + default_results = default(path) + default_results = default_results.keys if default_results + begin + real_results = real_store.list(path) + if default_results + (real_results + default_results).uniq + else + real_results + end + rescue DataNotFoundError + if default_results + default_results + else + raise + end + end + end + + def exists?(path) + real_store.exists?(path) || default(path) + end + + def exists_dir?(path) + real_store.exists_dir?(path) || default(path) + end + + def org_defaults(name) + { + 'clients' => { + "#{name}-validator" => '{ "validator": true }', + "#{name}-webui" => '{ "admin": true }' + }, + 'cookbooks' => {}, + 'data' => {}, + 'environments' => { + '_default' => '{ "description": "The default Chef environment" }' + }, + 'file_store' => { + 'checksums' => {} + }, + 'nodes' => {}, + 'roles' => {}, + 'sandboxes' => {}, + 'users' => { + 'admin' => '{ "admin": "true" }' + }, + + 'org' => '{}', + 'containers' => { + 'clients' => '{}', + 'containers' => '{}', + 'cookbooks' => '{}', + 'data' => '{}', + 'environments' => '{}', + 'groups' => '{}', + 'nodes' => '{}', + 'roles' => '{}', + 'sandboxes' => '{}' + }, + 'groups' => { + 'admins' => admins_group, + 'billing-admins' => '{}', + 'clients' => clients_group, + 'users' => users_group, + }, + 'acls' => { + 'clients' => {}, + 'containers' => { + 'cookbooks' => '{ + "create": { "groups": [ "admins", "users" ] }, + "read": { "groups": [ "admins", "users", "clients" ] }, + "update": { "groups": [ "admins", "users" ] }, + "delete": { "groups": [ "admins", "users" ] } + }', + 'environments' => '{ + "create": { "groups": [ "admins", "users" ] }, + "read": { "groups": [ "admins", "users", "clients" ] }, + "update": { "groups": [ "admins", "users" ] }, + "delete": { "groups": [ "admins", "users" ] } + }', + 'roles' => '{ + "create": { "groups": [ "admins", "users" ] }, + "read": { "groups": [ "admins", "users", "clients" ] }, + "update": { "groups": [ "admins", "users" ] }, + "delete": { "groups": [ "admins", "users" ] } + }', + 'data' => '{ + "create": { "groups": [ "admins", "users", "clients" ] }, + "read": { "groups": [ "admins", "users", "clients" ] }, + "update": { "groups": [ "admins", "users", "clients" ] }, + "delete": { "groups": [ "admins", "users", "clients" ] }, + }', + 'nodes' => '{ + "create": { "groups": [ "admins", "users", "clients" ] }, + "read": { "groups": [ "admins", "users", "clients" ] }, + "update": { "groups": [ "admins", "users" ] }, + "delete": { "groups": [ "admins", "users" ] } + }', + 'clients' => client_container_acls, + 'groups' => '{ + "read": { "groups": [ "admins", "users" ] } + }', + 'containers' => '{ + "read": { "groups": [ "admins", "users" ] } + }', + 'sandboxes' => '{ + "create": { "groups": [ "admins", "users" ] } + }' + }, + 'cookbooks' => {}, + 'data' => {}, + 'environments' => {}, + 'groups' => { + 'billing-admins' => '{ + "create": { "groups": [ ] }, + "read": { "groups": [ "billing-admins" ] }, + "update": { "groups": [ "billing-admins" ] }, + "delete": { "groups": [ ] } + "grant": { "groups": [ ] } + }', + }, + 'nodes' => {}, + 'roles' => {}, + 'sandboxes' => {} + } + } + end + + def admins_group + proc do |data, path| + admins = data.list(path[0..1] + [ 'users' ]).select do |name| + user = JSON.parse(data.get(path[0..1] + [ 'users', name ]), :create_additions => false) + user['admin'] + end + admins += data.list(path[0..1] + [ 'clients' ]).select do |name| + client = JSON.parse(data.get(path[0..1] + [ 'clients', name ]), :create_additions => false) + client['admin'] + end + JSON.pretty_generate({ 'actors' => admins }) + end + end + + def clients_group + proc do |data, path| + clients = data.list(path[0..1] + [ 'clients' ]) + JSON.pretty_generate({ 'actors' => clients }) + end + end + + def users_group + proc do |data, path| + users = data.list(path[0..1] + [ 'users' ]) + JSON.pretty_generate({ 'users' => users }) + end + end + + def client_container_acls + proc do |data, path| + validators = data.list(path[0..1] + [ 'clients' ]).select do |name| + client = JSON.parse(data.get(path[0..1] + [ 'clients', name ]), :create_additions => false) + client['validator'] + end + + JSON.pretty_generate({ + 'create' => { "actors" => [ validators] }, + 'read' => { 'groups' => [ 'admins', 'users' ] }, + 'delete' => { 'groups' => [ 'admins', 'users' ] } + }) + end + end + end + end +end diff --git a/lib/chef_zero/data_store/interface_v1.rb b/lib/chef_zero/data_store/interface_v1.rb index acdb788..60df0e8 100644 --- a/lib/chef_zero/data_store/interface_v1.rb +++ b/lib/chef_zero/data_store/interface_v1.rb @@ -9,38 +9,51 @@ module ChefZero raise "clear not implemented by class #{self.class}" end + # Create a directory. + # options is a list of symbols, including: + # :recursive - create any parents needed def create_dir(path, name, *options) raise "create_dir not implemented by class #{self.class}" end + # Create a file. + # options is a list of symbols, including: + # :create_dir - create any parents needed def create(path, name, data, *options) raise "create not implemented by class #{self.class}" end + # Get a file. def get(path, request=nil) raise "get not implemented by class #{self.class}" end + # Set a file's value. def set(path, data, *options) raise "set not implemented by class #{self.class}" end + # Delete a file. def delete(path) raise "delete not implemented by class #{self.class}" end + # Delete a directory. def delete_dir(path, *options) raise "delete_dir not implemented by class #{self.class}" end + # List a directory. def list(path) raise "list not implemented by class #{self.class}" end + # Check a file's existence. def exists?(path) raise "exists? not implemented by class #{self.class}" end + # Check a directory's existence. def exists_dir?(path) raise "exists_dir? not implemented by class #{self.class}" end diff --git a/lib/chef_zero/data_store/memory_store_v2.rb b/lib/chef_zero/data_store/memory_store_v2.rb index a190c00..864d192 100644 --- a/lib/chef_zero/data_store/memory_store_v2.rb +++ b/lib/chef_zero/data_store/memory_store_v2.rb @@ -31,55 +31,6 @@ module ChefZero def clear @data = {} - - create_dir([], 'organizations') - end - - def create_org(name) - org = { - 'clients' => { - "#{name}-validator" => '{ "validator": true }', - "#{name}-webui" => '{ "admin": true }' - }, - 'cookbooks' => {}, - 'data' => {}, - 'environments' => { - '_default' => '{ "description": "The default Chef environment" }' - }, - 'file_store' => { - 'checksums' => {} - }, - 'org' => '{}', - 'containers' => { - 'clients' => '{}', - 'containers' => '{}', - 'cookbooks' => '{}', - 'data' => '{}', - 'environments' => '{}', - 'groups' => '{}', - 'nodes' => '{}', - 'roles' => '{}', - 'sandboxes' => '{}', - }, - 'acls' => { - 'clients' => {}, - 'containers' => {}, - 'cookbooks' => {}, - 'data' => {}, - 'environments' => {}, - 'groups' => {}, - 'nodes' => {}, - 'roles' => {}, - 'sandboxes' => {}, - }, - 'groups' => {}, - 'nodes' => {}, - 'roles' => {}, - 'sandboxes' => {}, - 'users' => { - 'admin' => '{ "admin": "true" }' - } - } end def create_dir(path, name, *options) @@ -90,7 +41,7 @@ module ChefZero raise DataAlreadyExistsError.new(path + [name]) end else - _create_dir(path, parent, name) + parent[name] = {} end end @@ -190,7 +141,7 @@ module ChefZero path.each_with_index do |path_part, index| if !value.has_key?(path_part) if create_dir - _create_dir(path[0,index], value, path_part) + value[path_part] = {} else raise DataNotFoundError.new(path[0,index+1]) end @@ -199,14 +150,6 @@ module ChefZero end value end - - def _create_dir(parent_path, parent, name) - if parent_path == [ 'organizations' ] - parent[name] = create_org(name) - else - parent[name] = {} - end - end end end end diff --git a/lib/chef_zero/data_store/v1_to_v2_adapter.rb b/lib/chef_zero/data_store/v1_to_v2_adapter.rb index b01941b..e081fd1 100644 --- a/lib/chef_zero/data_store/v1_to_v2_adapter.rb +++ b/lib/chef_zero/data_store/v1_to_v2_adapter.rb @@ -10,40 +10,15 @@ module ChefZero clear end - def self.org_defaults(name) - { - 'clients' => { - "#{name}-validator" => '{ "validator": true }', - "#{name}-webui" => '{ "admin": true }' - }, - 'environments' => { - '_default' => '{ "description": "The default Chef environment" }' - }, - 'users' => { - 'admin' => '{ "admin": "true" }' - } - } - end - - ORG_DEFAULTS = org_defaults('chef') - attr_reader :real_store attr_reader :single_org def clear - if @options[:org_defaults] - @defaults = { 'organizations' => { @single_org => @options[:org_defaults] }} - else - @defaults = {} - end real_store.clear if real_store.respond_to?(:clear) end def create_dir(path, name, *options) return nil if skip_organizations?(path, name) - if using_default?(path, name) - raise DataAlreadyExistsError.new(path + [name]) - end fix_exceptions do real_store.create_dir(path[2..-1], name, *options) end @@ -51,11 +26,6 @@ module ChefZero def create(path, name, data, *options) return nil if skip_organizations?(path, name) - if using_default?(path, name) - raise DataAlreadyExistsError.new(path + [name]) - end - remove_default(path, name) - fix_exceptions do real_store.create(path[2..-1], name, data, *options) end @@ -63,18 +33,13 @@ module ChefZero def get(path, request=nil) return nil if skip_organizations?(path) - if using_default?(path) - get_default(path) - else - fix_exceptions do - real_store.get(path[2..-1], request) - end + fix_exceptions do + real_store.get(path[2..-1], request) end end def set(path, data, *options) return nil if skip_organizations?(path) - remove_default(path) fix_exceptions do real_store.set(path[2..-1], data, *options) end @@ -82,7 +47,6 @@ module ChefZero def delete(path) return nil if skip_organizations?(path) - remove_default(path) fix_exceptions do real_store.delete(path[2..-1]) end @@ -98,75 +62,26 @@ module ChefZero def list(path) return nil if skip_organizations?(path) fix_exceptions do - result = real_store.list(path[2..-1]) - if using_default?(path) - result ||= [] - get_default(path).keys.each do |value| - result << value if !result.include?(value) - end - end - result + real_store.list(path[2..-1]) end end def exists?(path) return nil if skip_organizations?(path) - if using_default?(path) - true - else - fix_exceptions do - real_store.exists?(path[2..-1]) - end + fix_exceptions do + real_store.exists?(path[2..-1]) end end def exists_dir?(path) return nil if skip_organizations?(path) - if using_default?(path) - true - else - fix_exceptions do - real_store.exists_dir?(path[2..-1]) - end + fix_exceptions do + real_store.exists_dir?(path[2..-1]) end end private - def using_default?(path, name = nil) - path = path + [name] if name - result = @defaults - path.each do |part| - return false if !result.has_key?(part) - result = result[part] - end - !result.nil? - end - - def get_default(path, name = nil) - path = path + [name] if name - result = @defaults - path.each do |part| - return nil if !result.has_key?(part) - result = result[part] - end - result - end - - def remove_default(path, name = nil) - dir = name ? path[0..-2] : path - default = @defaults - dir.each do |part| - return if !default.has_key?(part) - default = default[part] - end - - name = name || path.last - if name - default.delete(name) - end - end - def fix_exceptions begin yield diff --git a/lib/chef_zero/server.rb b/lib/chef_zero/server.rb index e352e98..ec93d8a 100644 --- a/lib/chef_zero/server.rb +++ b/lib/chef_zero/server.rb @@ -30,6 +30,7 @@ require 'chef_zero/cookbook_data' require 'chef_zero/rest_router' require 'chef_zero/data_store/memory_store_v2' require 'chef_zero/data_store/v1_to_v2_adapter' +require 'chef_zero/data_store/default_facade' require 'chef_zero/version' require 'chef_zero/endpoints/rest_list_endpoint' @@ -125,12 +126,13 @@ module ChefZero # def data_store @data_store ||= begin - result = @options[:data_store] || DataStore::MemoryStoreV2.new + result = @options[:data_store] || DataStore::DefaultFacade.new(DataStore::MemoryStoreV2.new) if options[:single_org] if result.respond_to?(:interface_version) && result.interface_version >= 2 && result.interface_version < 3 result.create_dir([ 'organizations' ], options[:single_org]) else result = ChefZero::DataStore::V1ToV2Adapter.new(result, options[:single_org]) + result = ChefZero::DataStore::DefaultFacade.new(result) end else if !(result.respond_to?(:interface_version) && result.interface_version >= 2 && result.interface_version < 3) |