summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/chef_zero/data_normalizer.rb46
-rw-r--r--lib/chef_zero/data_store/default_facade.rb316
-rw-r--r--lib/chef_zero/data_store/interface_v1.rb13
-rw-r--r--lib/chef_zero/data_store/memory_store_v2.rb61
-rw-r--r--lib/chef_zero/data_store/v1_to_v2_adapter.rb99
-rw-r--r--lib/chef_zero/server.rb4
-rw-r--r--spec/run.rb2
7 files changed, 346 insertions, 195 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)
diff --git a/spec/run.rb b/spec/run.rb
index 8a6e2b0..0403059 100644
--- a/spec/run.rb
+++ b/spec/run.rb
@@ -23,7 +23,7 @@ def start_local_server(chef_repo_path)
Chef::Config.versioned_cookbooks = true
chef_fs = Chef::ChefFS::Config.new.local_fs
data_store = Chef::ChefFS::ChefFSDataStore.new(chef_fs)
- data_store = ChefZero::DataStore::V1ToV2Adapter.new(data_store, 'chef', :org_defaults => ChefZero::DataStore::V1ToV2Adapter::ORG_DEFAULTS)
+ data_store = ChefZero::DataStore::V1ToV2Adapter.new(data_store, 'chef')
server = ChefZero::Server.new(:port => 8889, :data_store => data_store)#, :log_level => :debug)
server.start_background
server