chef-zero 2.2.1 → 3.0.0.rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/Rakefile +5 -1
  4. data/bin/chef-zero +17 -0
  5. data/lib/chef_zero/chef_data/acl_path.rb +139 -0
  6. data/lib/chef_zero/chef_data/cookbook_data.rb +240 -0
  7. data/lib/chef_zero/chef_data/data_normalizer.rb +207 -0
  8. data/lib/chef_zero/chef_data/default_creator.rb +446 -0
  9. data/lib/chef_zero/data_store/default_facade.rb +149 -0
  10. data/lib/chef_zero/data_store/interface_v1.rb +18 -0
  11. data/lib/chef_zero/data_store/memory_store.rb +2 -1
  12. data/lib/chef_zero/data_store/memory_store_v2.rb +3 -36
  13. data/lib/chef_zero/data_store/raw_file_store.rb +147 -0
  14. data/lib/chef_zero/data_store/v1_to_v2_adapter.rb +39 -103
  15. data/lib/chef_zero/data_store/v2_to_v1_adapter.rb +1 -1
  16. data/lib/chef_zero/endpoints/acl_endpoint.rb +38 -0
  17. data/lib/chef_zero/endpoints/acls_endpoint.rb +29 -0
  18. data/lib/chef_zero/endpoints/actor_endpoint.rb +36 -10
  19. data/lib/chef_zero/endpoints/actors_endpoint.rb +38 -6
  20. data/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +15 -9
  21. data/lib/chef_zero/endpoints/container_endpoint.rb +22 -0
  22. data/lib/chef_zero/endpoints/containers_endpoint.rb +13 -0
  23. data/lib/chef_zero/endpoints/cookbook_endpoint.rb +1 -1
  24. data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +15 -14
  25. data/lib/chef_zero/endpoints/cookbooks_base.rb +2 -2
  26. data/lib/chef_zero/endpoints/data_bag_endpoint.rb +4 -4
  27. data/lib/chef_zero/endpoints/data_bag_item_endpoint.rb +5 -5
  28. data/lib/chef_zero/endpoints/data_bags_endpoint.rb +5 -4
  29. data/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +2 -2
  30. data/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +6 -6
  31. data/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +2 -2
  32. data/lib/chef_zero/endpoints/environment_endpoint.rb +5 -5
  33. data/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +2 -2
  34. data/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +3 -3
  35. data/lib/chef_zero/endpoints/environment_role_endpoint.rb +2 -2
  36. data/lib/chef_zero/endpoints/file_store_file_endpoint.rb +1 -1
  37. data/lib/chef_zero/endpoints/group_endpoint.rb +20 -0
  38. data/lib/chef_zero/endpoints/groups_endpoint.rb +13 -0
  39. data/lib/chef_zero/endpoints/license_endpoint.rb +25 -0
  40. data/lib/chef_zero/endpoints/node_endpoint.rb +5 -5
  41. data/lib/chef_zero/endpoints/not_found_endpoint.rb +2 -2
  42. data/lib/chef_zero/endpoints/organization_association_request_endpoint.rb +22 -0
  43. data/lib/chef_zero/endpoints/organization_association_requests_endpoint.rb +29 -0
  44. data/lib/chef_zero/endpoints/organization_authenticate_user_endpoint.rb +26 -0
  45. data/lib/chef_zero/endpoints/organization_endpoint.rb +41 -0
  46. data/lib/chef_zero/endpoints/organization_user_endpoint.rb +48 -0
  47. data/lib/chef_zero/endpoints/organization_users_endpoint.rb +14 -0
  48. data/lib/chef_zero/endpoints/organization_validator_key_endpoint.rb +20 -0
  49. data/lib/chef_zero/endpoints/organizations_endpoint.rb +55 -0
  50. data/lib/chef_zero/endpoints/principal_endpoint.rb +15 -3
  51. data/lib/chef_zero/endpoints/rest_list_endpoint.rb +8 -6
  52. data/lib/chef_zero/endpoints/rest_object_endpoint.rb +12 -10
  53. data/lib/chef_zero/endpoints/role_endpoint.rb +5 -5
  54. data/lib/chef_zero/endpoints/role_environments_endpoint.rb +2 -2
  55. data/lib/chef_zero/endpoints/sandbox_endpoint.rb +2 -2
  56. data/lib/chef_zero/endpoints/sandboxes_endpoint.rb +4 -4
  57. data/lib/chef_zero/endpoints/search_endpoint.rb +10 -10
  58. data/lib/chef_zero/endpoints/system_recovery_endpoint.rb +30 -0
  59. data/lib/chef_zero/endpoints/user_association_request_endpoint.rb +40 -0
  60. data/lib/chef_zero/endpoints/user_association_requests_count_endpoint.rb +19 -0
  61. data/lib/chef_zero/endpoints/user_association_requests_endpoint.rb +19 -0
  62. data/lib/chef_zero/endpoints/user_organizations_endpoint.rb +22 -0
  63. data/lib/chef_zero/rest_base.rb +79 -13
  64. data/lib/chef_zero/rest_error_response.rb +1 -1
  65. data/lib/chef_zero/rest_request.rb +4 -0
  66. data/lib/chef_zero/rest_router.rb +1 -0
  67. data/lib/chef_zero/rspec.rb +55 -8
  68. data/lib/chef_zero/server.rb +87 -21
  69. data/lib/chef_zero/version.rb +1 -1
  70. data/spec/run_oc_pedant.rb +53 -0
  71. data/spec/{run.rb → run_pedant.rb} +13 -4
  72. data/spec/server_spec.rb +54 -0
  73. data/spec/support/oc_pedant.rb +134 -0
  74. data/spec/support/pedant.rb +1 -1
  75. metadata +54 -13
  76. data/lib/chef_zero/cookbook_data.rb +0 -236
  77. data/lib/chef_zero/data_normalizer.rb +0 -146
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 34e265dc59ee4777d6ba4efdebaaf76a64c578f4
4
- data.tar.gz: 35725b7a8b56e88b423602d8f437b610f0e9ea7c
3
+ metadata.gz: 8c3b229916ca80eb41cf861bc986a0bbc8198b7f
4
+ data.tar.gz: b4579df1962991a5f8377528e8e296ac2b25a22c
5
5
  SHA512:
6
- metadata.gz: 052d0eefadf4f57e5cdc6de67f66707f8417af32bda63dee6287616ff0e940e67b491eb8b50b2903a3bbaf98b2a85cc4f2df303218f80d5d1e03a083ab287514
7
- data.tar.gz: 1654216d04708f136e8d84b2ead621cd1eb061f039b8d9499eac3b4c29bcf10e834790ed6c7622c37528a66f5187994b08099e1416ea591bd2f00ededc271fa6
6
+ metadata.gz: 96d614e76573211f42078149a7db53d41e455c211f9278166b8a2c4318dd68ef9d13eb00349a23fe980f54ee5dd5761fa12098ffdea89eee0cce1249d96f8ae9
7
+ data.tar.gz: e0ae0d2811a84bb800f2f46135f6279cb2eddef045491ed7764c42f137b208fc9d4b02bf10f8a9940e202aad81b87e7095435d49d8c37029d4659dbc502f5b9c
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Stories in Ready](https://badge.waffle.io/opscode/chef-zero.png?label=ready&title=Ready)](https://waffle.io/opscode/chef-zero)[![Stories in Progress](https://badge.waffle.io/opscode/chef-zero.png?label=in+progress&title=In+Progress)](https://waffle.io/opscode/chef-zero)
2
+
1
3
  Chef Zero
2
4
  =========
3
5
 
data/Rakefile CHANGED
@@ -10,7 +10,11 @@ task :spec do
10
10
  end
11
11
 
12
12
  task :pedant do
13
- require File.expand_path('spec/run')
13
+ require File.expand_path('spec/run_pedant')
14
+ end
15
+
16
+ task :oc_pedant do
17
+ require File.expand_path('spec/run_oc_pedant')
14
18
  end
15
19
 
16
20
  task :chef_spec do
@@ -8,6 +8,7 @@ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
8
8
 
9
9
  require 'chef_zero/version'
10
10
  require 'chef_zero/server'
11
+ require 'chef_zero/data_store/raw_file_store'
11
12
  require 'optparse'
12
13
 
13
14
  def parse_port(port)
@@ -49,6 +50,18 @@ OptionParser.new do |opts|
49
50
  options[:log_level] = value
50
51
  end
51
52
 
53
+ opts.on("--multi-org", "Whether to run in multi-org mode") do |value|
54
+ options[:single_org] = nil
55
+ end
56
+
57
+ opts.on("--file-store PATH", "Persist data to files at the given path") do |value|
58
+ options[:data_store] = ChefZero::DataStore::RawFileStore.new(value)
59
+ end
60
+
61
+ opts.on("--[no-]ssl", "Use SSL with self-signed certificate(Auto generate before every run). Default: false.") do |value|
62
+ options[:ssl] = value
63
+ end
64
+
52
65
  opts.on_tail("-h", "--help", "Show this message") do
53
66
  puts opts
54
67
  exit
@@ -60,6 +73,10 @@ OptionParser.new do |opts|
60
73
  end
61
74
  end.parse!
62
75
 
76
+ if options[:data_store]
77
+ options[:data_store] = ChefZero::DataStore::DefaultFacade.new(options[:data_store], options[:single_org], false)
78
+ end
79
+
63
80
  server = ChefZero::Server.new(options)
64
81
 
65
82
  if options[:daemon]
@@ -0,0 +1,139 @@
1
+ module ChefZero
2
+ module ChefData
3
+ # Manages translations between REST and ACL data paths
4
+ # and parent paths.
5
+ #
6
+ # Suggestions
7
+ # - make /organizations/ORG/_acl and deprecate organization/_acl and organizations/_acl
8
+ # - add endpoints for /containers/(users|organizations|containers)(/_acl)
9
+ # - add PUT for */_acl
10
+ # - add endpoints for /organizations/ORG/data/containers and /organizations/ORG/cookbooks/containers
11
+ # - sane, fully documented ACL model
12
+ # - sane inheritance / override model: if actors or groups are explicitly
13
+ # specified on X, they are not inherited from X's parent
14
+ # - stop adding pivotal to acls (he already has access to what he needs)
15
+ module AclPath
16
+ ORG_DATA_TYPES = %w(clients cookbooks containers data environments groups nodes roles sandboxes)
17
+ TOP_DATA_TYPES = %w(containers organizations users)
18
+
19
+ # ACL data paths for a partition are:
20
+ # / -> /acls/root
21
+ # /TYPE -> /acls/containers/TYPE
22
+ # /TYPE/NAME -> /acls/TYPE/NAME
23
+ #
24
+ # The root partition "/" has its own acls, so it looks like this:
25
+ #
26
+ # / -> /acls/root
27
+ # /users -> /acls/containers/users
28
+ # /organizations -> /acls/containers/organizations
29
+ # /users/schlansky -> /acls/users/schlansky
30
+ #
31
+ # Each organization is its own partition, so it looks like this:
32
+ #
33
+ # /organizations/blah -> /organizations/blah/acls/root
34
+ # /organizations/blah/roles -> /organizations/blah/acls/containers/roles
35
+ # /organizations/blah/roles/web -> /organizations/blah/acls/roles/web
36
+ # /organizations/ORG is its own partition. ACLs for anything under it follow
37
+
38
+ # This method takes a Chef REST path and returns the chef-zero path
39
+ # used to look up the ACL. If an object does not have an ACL directly,
40
+ # it will return nil. Paths like /organizations/ORG/data/bag/item will
41
+ # return nil, because it is the parent path (data/bag) that has an ACL.
42
+ def self.get_acl_data_path(path)
43
+ # Things under organizations have their own acls hierarchy
44
+ if path[0] == 'organizations' && path.size >= 2
45
+ under_org = partition_acl_data_path(path[2..-1], ORG_DATA_TYPES)
46
+ if under_org
47
+ path[0..1] + under_org
48
+ end
49
+ else
50
+ partition_acl_data_path(path, TOP_DATA_TYPES)
51
+ end
52
+ end
53
+
54
+ #
55
+ # Reverse transform from acl_data_path to path.
56
+ # /acls/root -> /
57
+ # /acls/** -> /**
58
+ # /organizations/ORG/acls/root -> /organizations/ORG
59
+ # /organizations/ORG/acls/** -> /organizations/ORG/**
60
+ #
61
+ # This means that /acls/containers/nodes maps to
62
+ # /containers/nodes, not /nodes.
63
+ #
64
+ def self.get_object_path(acl_data_path)
65
+ if acl_data_path[0] == 'acls'
66
+ if acl_data_path[1] == 'root'
67
+ []
68
+ else
69
+ acl_data_path[1..-1]
70
+ end
71
+ elsif acl_data_path[0] == 'organizations' && acl_data_path[2] == 'acls'
72
+ if acl_data_path[3] == 'root'
73
+ acl_data_path[0..1]
74
+ else
75
+ acl_data_path[0..1] + acl_data_path[3..-1]
76
+ end
77
+ end
78
+ end
79
+
80
+ # Method *assumes* acl_data_path is valid.
81
+ # /organizations/BLAH's parent is /organizations
82
+ #
83
+ # An example traversal up the whole tree:
84
+ # /organizations/foo/acls/nodes/mario ->
85
+ # /organizations/foo/acls/containers/nodes ->
86
+ # /organizations/foo/acls/containers/containers ->
87
+ # /organizations/foo/acls/root ->
88
+ # /acls/containers/organizations ->
89
+ # /acls/containers/containers ->
90
+ # /acls/root ->
91
+ # nil
92
+ def self.parent_acl_data_path(acl_data_path)
93
+ if acl_data_path[0] == 'organizations'
94
+ under_org = partition_parent_acl_data_path(acl_data_path[2..-1])
95
+ if under_org
96
+ acl_data_path[0..1] + under_org
97
+ else
98
+ # ACL data path is /organizations/X/acls/root; therefore parent is "/organizations"
99
+ [ 'acls', 'containers', 'organizations' ]
100
+ end
101
+ else
102
+ partition_parent_acl_data_path(acl_data_path)
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ # /acls/root -> nil
109
+ # /acls/containers/containers -> /acls/root
110
+ # /acls/TYPE/X -> /acls/containers/TYPE
111
+ #
112
+ # Method *assumes* acl_data_path is valid.
113
+ # Returns nil if the path is /acls/root
114
+ def self.partition_parent_acl_data_path(acl_data_path)
115
+ if acl_data_path.size == 3
116
+ if acl_data_path == %w(acls containers containers)
117
+ [ 'acls', 'root' ]
118
+ else
119
+ [ 'acls', 'containers', acl_data_path[1]]
120
+ end
121
+ else
122
+ nil
123
+ end
124
+ end
125
+
126
+ def self.partition_acl_data_path(path, data_types)
127
+ if path.size == 0
128
+ [ 'acls', 'root']
129
+ elsif data_types.include?(path[0])
130
+ if path.size == 0
131
+ [ 'acls', 'containers', path[0] ]
132
+ elsif path.size == 2
133
+ [ 'acls', path[0], path[1] ]
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,240 @@
1
+ require 'digest/md5'
2
+ require 'hashie/mash'
3
+
4
+ module ChefZero
5
+ module ChefData
6
+ module CookbookData
7
+ def self.to_hash(cookbook, name, version=nil)
8
+ frozen = false
9
+ if cookbook.has_key?(:frozen)
10
+ frozen = cookbook[:frozen]
11
+ cookbook = cookbook.dup
12
+ cookbook.delete(:frozen)
13
+ end
14
+
15
+ result = files_from(cookbook)
16
+ recipe_names = result[:recipes].map do |recipe|
17
+ recipe_name = recipe[:name][0..-2]
18
+ recipe_name == 'default' ? name : "#{name}::#{recipe_name}"
19
+ end
20
+ result[:metadata] = metadata_from(cookbook, name, version, recipe_names)
21
+ result[:name] = "#{name}-#{result[:metadata][:version]}"
22
+ result[:json_class] = 'Chef::CookbookVersion'
23
+ result[:cookbook_name] = name
24
+ result[:version] = result[:metadata][:version]
25
+ result[:chef_type] = 'cookbook_version'
26
+ result[:frozen?] = true if frozen
27
+ result
28
+ end
29
+
30
+ def self.metadata_from(directory, name, version, recipe_names)
31
+ metadata = PretendCookbookMetadata.new(PretendCookbook.new(name, recipe_names))
32
+ # If both .rb and .json exist, read .rb
33
+ # TODO if recipes has 3 recipes in it, and the Ruby/JSON has only one, should
34
+ # the resulting recipe list have 1, or 3-4 recipes in it?
35
+ if has_child(directory, 'metadata.rb')
36
+ begin
37
+ file = filename(directory, 'metadata.rb') || "(#{name}/metadata.rb)"
38
+ metadata.instance_eval(read_file(directory, 'metadata.rb'), file)
39
+ rescue
40
+ ChefZero::Log.error("Error loading cookbook #{name}: #{$!}\n #{$!.backtrace.join("\n ")}")
41
+ end
42
+ elsif has_child(directory, 'metadata.json')
43
+ metadata.from_json(read_file(directory, 'metadata.json'))
44
+ end
45
+ result = {}
46
+ metadata.to_hash.each_pair do |key,value|
47
+ result[key.to_sym] = value
48
+ end
49
+ result[:version] = version if version
50
+ result
51
+ end
52
+
53
+ private
54
+
55
+ # Just enough cookbook to make a Metadata object
56
+ class PretendCookbook
57
+ def initialize(name, fully_qualified_recipe_names)
58
+ @name = name
59
+ @fully_qualified_recipe_names = fully_qualified_recipe_names
60
+ end
61
+ attr_reader :name, :fully_qualified_recipe_names
62
+ end
63
+
64
+ # Handles loading configuration values from a Chef config file
65
+ #
66
+ # @author Justin Campbell <justin.campbell@riotgames.com>
67
+ class PretendCookbookMetadata < Hash
68
+ # @param [String] path
69
+ def initialize(cookbook)
70
+ self.name(cookbook.name)
71
+ self.recipes(cookbook.fully_qualified_recipe_names)
72
+ %w(attributes grouping dependencies supports recommendations suggestions conflicting providing replacing recipes).each do |hash_arg|
73
+ self[hash_arg.to_sym] = Hashie::Mash.new
74
+ end
75
+ end
76
+
77
+ def from_json(json)
78
+ self.merge!(JSON.parse(json))
79
+ end
80
+
81
+ private
82
+
83
+ def depends(cookbook, *version_constraints)
84
+ cookbook_arg(:dependencies, cookbook, version_constraints)
85
+ end
86
+
87
+ def supports(cookbook, *version_constraints)
88
+ cookbook_arg(:supports, cookbook, version_constraints)
89
+ end
90
+
91
+ def recommends(cookbook, *version_constraints)
92
+ cookbook_arg(:recommendations, cookbook, version_constraints)
93
+ end
94
+
95
+ def suggests(cookbook, *version_constraints)
96
+ cookbook_arg(:suggestions, cookbook, version_constraints)
97
+ end
98
+
99
+ def conflicts(cookbook, *version_constraints)
100
+ cookbook_arg(:conflicting, cookbook, version_constraints)
101
+ end
102
+
103
+ def provides(cookbook, *version_constraints)
104
+ cookbook_arg(:providing, cookbook, version_constraints)
105
+ end
106
+
107
+ def replaces(cookbook, *version_constraints)
108
+ cookbook_arg(:replacing, cookbook, version_constraints)
109
+ end
110
+
111
+ def recipe(recipe, description)
112
+ self[:recipes][recipe] = description
113
+ end
114
+
115
+ def attribute(name, options)
116
+ self[:attributes][name] = options
117
+ end
118
+
119
+ def grouping(name, options)
120
+ self[:grouping][name] = options
121
+ end
122
+
123
+ def cookbook_arg(key, cookbook, version_constraints)
124
+ self[key][cookbook] = version_constraints.first || ">= 0.0.0"
125
+ end
126
+
127
+ def method_missing(key, value = nil)
128
+ if value.nil?
129
+ self[key.to_sym]
130
+ else
131
+ store key.to_sym, value
132
+ end
133
+ end
134
+ end
135
+
136
+ def self.files_from(directory)
137
+ # TODO some support .rb only
138
+ result = {
139
+ :attributes => load_child_files(directory, 'attributes', false),
140
+ :definitions => load_child_files(directory, 'definitions', false),
141
+ :recipes => load_child_files(directory, 'recipes', false),
142
+ :libraries => load_child_files(directory, 'libraries', false),
143
+ :templates => load_child_files(directory, 'templates', true),
144
+ :files => load_child_files(directory, 'files', true),
145
+ :resources => load_child_files(directory, 'resources', true),
146
+ :providers => load_child_files(directory, 'providers', true),
147
+ :root_files => load_files(directory, false)
148
+ }
149
+ set_specificity(result[:templates])
150
+ set_specificity(result[:files])
151
+ result
152
+ end
153
+
154
+ def self.has_child(directory, name)
155
+ if directory.is_a?(Hash)
156
+ directory.has_key?(name)
157
+ else
158
+ directory.child(name).exists?
159
+ end
160
+ end
161
+
162
+ def self.read_file(directory, name)
163
+ if directory.is_a?(Hash)
164
+ directory[name]
165
+ else
166
+ directory.child(name).read
167
+ end
168
+ end
169
+
170
+ def self.filename(directory, name)
171
+ if directory.respond_to?(:file_path)
172
+ File.join(directory.file_path, name)
173
+ else
174
+ nil
175
+ end
176
+ end
177
+
178
+ def self.get_directory(directory, name)
179
+ if directory.is_a?(Hash)
180
+ directory[name].is_a?(Hash) ? directory[name] : nil
181
+ else
182
+ result = directory.child(name)
183
+ result.dir? ? result : nil
184
+ end
185
+ end
186
+
187
+ def self.list(directory)
188
+ if directory.is_a?(Hash)
189
+ directory.keys
190
+ else
191
+ directory.children.map { |c| c.name }
192
+ end
193
+ end
194
+
195
+ def self.load_child_files(parent, key, recursive)
196
+ result = load_files(get_directory(parent, key), recursive)
197
+ result.each do |file|
198
+ file[:path] = "#{key}/#{file[:path]}"
199
+ end
200
+ result
201
+ end
202
+
203
+ def self.load_files(directory, recursive)
204
+ result = []
205
+ if directory
206
+ list(directory).each do |child_name|
207
+ dir = get_directory(directory, child_name)
208
+ if dir
209
+ if recursive
210
+ result += load_child_files(directory, child_name, recursive)
211
+ end
212
+ else
213
+ result += load_file(read_file(directory, child_name), child_name)
214
+ end
215
+ end
216
+ end
217
+ result
218
+ end
219
+
220
+ def self.load_file(value, name)
221
+ [{
222
+ :name => name,
223
+ :path => name,
224
+ :checksum => Digest::MD5.hexdigest(value),
225
+ :specificity => 'default'
226
+ }]
227
+ end
228
+
229
+ def self.set_specificity(files)
230
+ files.each do |file|
231
+ parts = file[:path].split('/')
232
+ raise "Only directories are allowed directly under templates or files: #{file[:path]}" if parts.size == 2
233
+ file[:specificity] = parts[1]
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ CookbookData = ChefData::CookbookData
240
+ end