chef-zero 4.3.2 → 4.4.0

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.
@@ -0,0 +1,24 @@
1
+ require 'chef_zero/chef_data/data_normalizer'
2
+
3
+ module ChefZero
4
+ module Endpoints
5
+ # /organizations/ORG/policies/NAME
6
+ class PolicyEndpoint < RestBase
7
+ # GET /organizations/ORG/policies/NAME
8
+ def get(request)
9
+ revisions = list_data(request, request.rest_path + ["revisions"])
10
+ data = { revisions: hashify_list(revisions) }
11
+ return json_response(200, data)
12
+ end
13
+
14
+ # DELETE /organizations/ORG/policies/NAME
15
+ def delete(request)
16
+ revisions = list_data(request, request.rest_path + ["revisions"])
17
+ data = { revisions: hashify_list(revisions) }
18
+
19
+ delete_data_dir(request, nil, :recursive)
20
+ return json_response(200, data)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/rest_base'
3
+ require 'chef_zero/chef_data/data_normalizer'
4
+
5
+ module ChefZero
6
+ module Endpoints
7
+ # /organizations/ORG/policy_groups/NAME
8
+ class PolicyGroupEndpoint < RestBase
9
+
10
+ # GET /organizations/ORG/policy_groups/NAME
11
+ def get(request)
12
+ data = {
13
+ uri: build_uri(request.base_uri, request.rest_path),
14
+ policies: get_policy_group_policies(request)
15
+ }
16
+ json_response(200, data)
17
+ end
18
+
19
+ # build a hash of {"some_policy_name"=>{"revision_id"=>"909c26701e291510eacdc6c06d626b9fa5350d25"}}
20
+ def get_policy_group_policies(request)
21
+ policies_revisions = {}
22
+
23
+ policies_path = request.rest_path + ["policies"]
24
+ policy_names = list_data(request, policies_path)
25
+ policy_names.each do |policy_name|
26
+ revision = parse_json(get_data(request, policies_path + [policy_name]))
27
+ policies_revisions[policy_name] = { revision_id: revision}
28
+ end
29
+
30
+ policies_revisions
31
+ end
32
+
33
+ # DELETE /organizations/ORG/policy_groups/NAME
34
+ def delete(request)
35
+ policy_group_policies = get_policy_group_policies(request)
36
+ delete_data_dir(request, nil, :recursive)
37
+
38
+ data = {
39
+ uri: build_uri(request.base_uri, request.rest_path),
40
+ policies: policy_group_policies
41
+ }
42
+ json_response(200, data)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,84 @@
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/rest_base'
3
+ require 'chef_zero/chef_data/data_normalizer'
4
+
5
+ module ChefZero
6
+ module Endpoints
7
+ # /organizations/ORG/policy_groups/GROUP/policies/NAME
8
+ #
9
+ # in the data store, this REST path actually stores the revision ID of ${policy_name} that's currently
10
+ # associated with ${policy_group}.
11
+ class PolicyGroupPolicyEndpoint < RestBase
12
+
13
+ # GET /organizations/ORG/policy_groups/GROUP/policies/NAME
14
+ def get(request)
15
+ policy_name = request.rest_path[5]
16
+
17
+ # fetch /organizations/{organization}/policies/{policy_name}/revisions/{revision_id}
18
+ revision_id = parse_json(get_data(request))
19
+ result = get_data(request, request.rest_path[0..1] +
20
+ ["policies", policy_name, "revisions", revision_id])
21
+ result = ChefData::DataNormalizer.normalize_policy(parse_json(result), policy_name, revision_id)
22
+ json_response(200, result)
23
+ end
24
+
25
+ # Create or update the policy document for the given policy group and policy name. If no policy group
26
+ # with the given name exists, it will be created. If no policy with the given revision_id exists, it
27
+ # will be created from the document in the request body. If a policy with that revision_id exists, the
28
+ # Chef Server simply associates that revision id with the given policy group. When successful, the
29
+ # document that was created or updated is returned.
30
+
31
+ ## MANDATORY FIELDS AND FORMATS
32
+ # * `revision_id`: String; Must be < 255 chars, matches /^[\-[:alnum:]_\.\:]+$/
33
+ # * `name`: String; Must match name in URI; Must be <= 255 chars, matches /^[\-[:alnum:]_\.\:]+$/
34
+ # * `run_list`: Array
35
+ # * `run_list[i]`: Fully Qualified Recipe Run List Item
36
+ # * `cookbook_locks`: JSON Object
37
+ # * `cookbook_locks(key)`: CookbookName
38
+ # * `cookbook_locks[item]`: JSON Object, mandatory keys: "identifier", "dotted_decimal_identifier"
39
+ # * `cookbook_locks[item]["identifier"]`: varchar(255) ?
40
+ # * `cookbook_locks[item]["dotted_decimal_identifier"]` ChefCompatibleVersionNumber
41
+
42
+ # PUT /organizations/ORG/policy_groups/GROUP/policies/NAME
43
+ def put(request)
44
+ policyfile_data = parse_json(request.body)
45
+ policy_name = request.rest_path[5]
46
+ revision_id = policyfile_data["revision_id"]
47
+
48
+ # If the policy revision being submitted does not exist, create it.
49
+ # Storage: /organizations/ORG/policies/POLICY/revisions/REVISION
50
+ policyfile_path = request.rest_path[0..1] + ["policies", policy_name, "revisions", revision_id]
51
+ if !exists_data?(request, policyfile_path)
52
+ create_data(request, policyfile_path[0..-2], revision_id, request.body, :create_dir)
53
+ end
54
+
55
+ # if named policy exists and the given revision ID exists, associate the revision ID with the policy
56
+ # group.
57
+ # Storage: /organizations/ORG/policies/POLICY/revisions/REVISION
58
+ response_code = exists_data?(request) ? 200 : 201
59
+ set_data(request, nil, to_json(revision_id), :create_dir)
60
+
61
+ already_json_response(response_code, request.body)
62
+ end
63
+
64
+ # DELETE /organizations/ORG/policy_groups/GROUP/policies/NAME
65
+ def delete(request)
66
+ # Save the existing association.
67
+ current_revision_id = parse_json(get_data(request))
68
+
69
+ # delete the association.
70
+ delete_data(request)
71
+
72
+ # return the full policy document at the no-longer-associated revision.
73
+ policy_name = request.rest_path[5]
74
+ policy_path = request.rest_path[0..1] + ["policies", policy_name,
75
+ "revisions", current_revision_id]
76
+
77
+
78
+ full_policy_doc = parse_json(get_data(request, policy_path))
79
+ full_policy_doc = ChefData::DataNormalizer.normalize_policy(full_policy_doc, policy_name, current_revision_id)
80
+ return json_response(200, full_policy_doc)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,38 @@
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/rest_base'
3
+ require 'chef_zero/chef_data/data_normalizer'
4
+
5
+ module ChefZero
6
+ module Endpoints
7
+ # /organizations/ORG/policy_groups
8
+ #
9
+ # in the data store, this REST path actually stores the revision ID of ${policy_name} that's currently
10
+ # associated with ${policy_group}.
11
+ class PolicyGroupsEndpoint < RestBase
12
+ # GET /organizations/ORG/policy_groups
13
+ def get(request)
14
+ # each policy group has policies and associated revisions under
15
+ # /policy_groups/{group name}/policies/{policy name}.
16
+ response_data = {}
17
+ list_data(request).each do |group_name|
18
+ group_path = request.rest_path + [group_name]
19
+ policy_list = list_data(request, group_path + ["policies"])
20
+
21
+ # build the list of policies with their revision ID associated with this policy group.
22
+ policies = {}
23
+ policy_list.each do |policy_name|
24
+ revision_id = parse_json(get_data(request, group_path + ["policies", policy_name]))
25
+ policies[policy_name] = { revision_id: revision_id }
26
+ end
27
+
28
+ response_data[group_name] = {
29
+ uri: build_uri(request.base_uri, group_path)
30
+ }
31
+ response_data[group_name][:policies] = policies unless policies.empty?
32
+ end
33
+
34
+ json_response(200, response_data)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ require 'chef_zero/chef_data/data_normalizer'
2
+
3
+ module ChefZero
4
+ module Endpoints
5
+ # /organizations/ORG/policies/NAME/revisions/REVISION
6
+ class PolicyRevisionEndpoint < RestBase
7
+ # GET /organizations/ORG/policies/NAME/revisions/REVISION
8
+ def get(request)
9
+ data = parse_json(get_data(request))
10
+ data = ChefData::DataNormalizer.normalize_policy(data, request.rest_path[3], request.rest_path[5])
11
+ return json_response(200, data)
12
+ end
13
+
14
+ # DELETE /organizations/ORG/policies/NAME/revisions/REVISION
15
+ def delete(request)
16
+ policyfile_data = parse_json(get_data(request))
17
+ policyfile_data = ChefData::DataNormalizer.normalize_policy(policyfile_data, request.rest_path[3], request.rest_path[5])
18
+ delete_data(request)
19
+ return json_response(200, policyfile_data)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ require 'chef_zero/chef_data/data_normalizer'
2
+
3
+ module ChefZero
4
+ module Endpoints
5
+ # /organizations/ORG/policies/NAME/revisions
6
+ class PolicyRevisionsEndpoint < RestBase
7
+ # POST /organizations/ORG/policies/NAME/revisions
8
+ def post(request)
9
+ policyfile_data = parse_json(request.body)
10
+ create_data(request, request.rest_path, policyfile_data["revision_id"], request.body, :create_dir)
11
+ return already_json_response(201, request.body)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -8,16 +8,20 @@ module ChefZero
8
8
  class PrincipalEndpoint < RestBase
9
9
  def get(request)
10
10
  name = request.rest_path[-1]
11
+ # If /organizations/ORG/users/NAME exists, use this user (only org members have precedence over clients). hey are an org member.
11
12
  json = get_data(request, request.rest_path[0..1] + [ 'users', name ], :nil)
12
13
  if json
13
14
  type = 'user'
14
15
  org_member = true
15
16
  else
17
+ # If /organizations/ORG/clients/NAME exists, use the client.
16
18
  json = get_data(request, request.rest_path[0..1] + [ 'clients', name ], :nil)
17
19
  if json
18
20
  type = 'client'
19
21
  org_member = true
20
22
  else
23
+ # If there is no client with that name, check for a user (/users/NAME) and return that with
24
+ # org_member = false.
21
25
  json = get_data(request, [ 'users', name ], :nil)
22
26
  if json
23
27
  type = 'user'
@@ -195,12 +195,13 @@ module ChefZero
195
195
  data_store.exists_dir?(rest_path)
196
196
  end
197
197
 
198
- def error(response_code, error)
199
- json_response(response_code, {"error" => [error]})
198
+ def error(response_code, error, opts={})
199
+ json_response(response_code, {"error" => [error]}, 0, 0, opts)
200
200
  end
201
201
 
202
- def json_response(response_code, json, request_version=0, response_version=0)
203
- already_json_response(response_code, FFI_Yajl::Encoder.encode(json, :pretty => true), request_version, response_version)
202
+ def json_response(response_code, json, request_version=0, response_version=0, opts={pretty: true})
203
+ do_pretty_json = opts[:pretty] && true
204
+ already_json_response(response_code, FFI_Yajl::Encoder.encode(json, :pretty => do_pretty_json), request_version, response_version)
204
205
  end
205
206
 
206
207
  def text_response(response_code, text)
@@ -238,5 +239,39 @@ module ChefZero
238
239
  def populate_defaults(request, response)
239
240
  response
240
241
  end
242
+
243
+ def parse_json(json)
244
+ FFI_Yajl::Parser.parse(json, create_additions: false)
245
+ end
246
+
247
+ def to_json(data)
248
+ FFI_Yajl::Encoder.encode(data, :pretty => true)
249
+ end
250
+
251
+ def get_data_or_else(request, path, or_else_value)
252
+ if exists_data?(request, path)
253
+ parse_json(get_data(request, path))
254
+ else
255
+ or_else_value
256
+ end
257
+ end
258
+
259
+ def list_data_or_else(request, path, or_else_value)
260
+ if exists_data_dir?(request, path)
261
+ list_data(request, path)
262
+ else
263
+ or_else_value
264
+ end
265
+ end
266
+
267
+ def hashify_list(list)
268
+ list.reduce({}) { |acc, obj| acc.merge( obj => {} ) }
269
+ end
270
+
271
+ def policy_name_invalid?(name)
272
+ !name.is_a?(String) ||
273
+ name.size > 255 ||
274
+ name =~ /[+ !]/
275
+ end
241
276
  end
242
277
  end
@@ -125,6 +125,10 @@ module ChefZero
125
125
  end
126
126
  end
127
127
 
128
+ def cookbook_artifact(name, data, &block)
129
+ before(chef_server_options[:server_scope]) { cookbook_artifact(name, data, &block) }
130
+ end
131
+
128
132
  def data_bag(name, data, &block)
129
133
  before(chef_server_options[:server_scope]) { data_bag(name, data, &block) }
130
134
  end
@@ -149,6 +153,14 @@ module ChefZero
149
153
  before(chef_server_options[:server_scope]) { org_member(*usernames) }
150
154
  end
151
155
 
156
+ def policy(name, data, &block)
157
+ before(chef_server_options[:server_scope]) { policy(name, data, &block) }
158
+ end
159
+
160
+ def policy_group(name, data, &block)
161
+ before(chef_server_options[:server_scope]) { policy_group(name, data, &block) }
162
+ end
163
+
152
164
  def role(name, data, &block)
153
165
  before(chef_server_options[:server_scope]) { role(name, data, &block) }
154
166
  end
@@ -215,6 +227,13 @@ module ChefZero
215
227
  end
216
228
  end
217
229
 
230
+ def cookbook_artifact(name, identifier, data, &block)
231
+ with_object_path("cookbook_artifacts/#{name}") do
232
+ ChefZero::RSpec.server.load_data({ 'cookbook_artifact' => { name => { identifier => data } } }, current_org)
233
+ instance_eval(&block) if block_given?
234
+ end
235
+ end
236
+
218
237
  def data_bag(name, data, &block)
219
238
  with_object_path("data/#{name}") do
220
239
  ChefZero::RSpec.server.load_data({ 'data' => { name => data }}, current_org)
@@ -251,6 +270,20 @@ module ChefZero
251
270
  ChefZero::RSpec.server.load_data({ 'members' => usernames }, current_org)
252
271
  end
253
272
 
273
+ def policy(name, version, data, &block)
274
+ with_object_path("policies/#{name}") do
275
+ ChefZero::RSpec.server.load_data({ 'policies' => { name => { version => data } } }, current_org)
276
+ instance_eval(&block) if block_given?
277
+ end
278
+ end
279
+
280
+ def policy_group(name, data, &block)
281
+ with_object_path("policy_groups/#{name}") do
282
+ ChefZero::RSpec.server.load_data({ 'policy_groups' => { name => data } }, current_org)
283
+ instance_eval(&block) if block_given?
284
+ end
285
+ end
286
+
254
287
  def role(name, data, &block)
255
288
  with_object_path("roles/#{name}") do
256
289
  ChefZero::RSpec.server.load_data({ 'roles' => { name => data } }, current_org)
@@ -45,8 +45,12 @@ require 'chef_zero/endpoints/actor_endpoint'
45
45
  require 'chef_zero/endpoints/cookbooks_endpoint'
46
46
  require 'chef_zero/endpoints/cookbook_endpoint'
47
47
  require 'chef_zero/endpoints/cookbook_version_endpoint'
48
+ require 'chef_zero/endpoints/cookbook_artifacts_cookbook_endpoint'
49
+ require 'chef_zero/endpoints/cookbook_artifacts_cookbook_identifier'
50
+ require 'chef_zero/endpoints/cookbook_artifacts_endpoint'
48
51
  require 'chef_zero/endpoints/containers_endpoint'
49
52
  require 'chef_zero/endpoints/container_endpoint'
53
+ require 'chef_zero/endpoints/dummy_endpoint'
50
54
  require 'chef_zero/endpoints/data_bags_endpoint'
51
55
  require 'chef_zero/endpoints/data_bag_endpoint'
52
56
  require 'chef_zero/endpoints/data_bag_item_endpoint'
@@ -61,6 +65,7 @@ require 'chef_zero/endpoints/environment_recipes_endpoint'
61
65
  require 'chef_zero/endpoints/environment_role_endpoint'
62
66
  require 'chef_zero/endpoints/license_endpoint'
63
67
  require 'chef_zero/endpoints/node_endpoint'
68
+ require 'chef_zero/endpoints/nodes_endpoint'
64
69
  require 'chef_zero/endpoints/node_identifiers_endpoint'
65
70
  require 'chef_zero/endpoints/organizations_endpoint'
66
71
  require 'chef_zero/endpoints/organization_endpoint'
@@ -70,8 +75,14 @@ require 'chef_zero/endpoints/organization_authenticate_user_endpoint'
70
75
  require 'chef_zero/endpoints/organization_users_endpoint'
71
76
  require 'chef_zero/endpoints/organization_user_endpoint'
72
77
  require 'chef_zero/endpoints/organization_validator_key_endpoint'
73
- require 'chef_zero/endpoints/principal_endpoint'
74
78
  require 'chef_zero/endpoints/policies_endpoint'
79
+ require 'chef_zero/endpoints/policy_endpoint'
80
+ require 'chef_zero/endpoints/policy_revisions_endpoint'
81
+ require 'chef_zero/endpoints/policy_revision_endpoint'
82
+ require 'chef_zero/endpoints/policy_groups_endpoint'
83
+ require 'chef_zero/endpoints/policy_group_endpoint'
84
+ require 'chef_zero/endpoints/policy_group_policy_endpoint'
85
+ require 'chef_zero/endpoints/principal_endpoint'
75
86
  require 'chef_zero/endpoints/role_endpoint'
76
87
  require 'chef_zero/endpoints/role_environments_endpoint'
77
88
  require 'chef_zero/endpoints/sandboxes_endpoint'
@@ -89,6 +100,7 @@ require 'chef_zero/endpoints/version_endpoint'
89
100
  require 'chef_zero/endpoints/server_api_version_endpoint'
90
101
 
91
102
  module ChefZero
103
+
92
104
  class Server
93
105
 
94
106
  DEFAULT_OPTIONS = {
@@ -443,6 +455,24 @@ module ChefZero
443
455
  end
444
456
  end
445
457
 
458
+ if contents['policies']
459
+ contents['policies'].each_pair do |policy_name, policy_struct|
460
+ # data_store.create_dir(['organizations', org_name, 'policies', policy_name], "revisions", :recursive)
461
+ dejsonize_children(policy_struct).each do |revision, policy_data|
462
+ data_store.set(['organizations', org_name, 'policies', policy_name,
463
+ "revisions", revision], policy_data, :create, :create_dir)
464
+ end
465
+ end
466
+ end
467
+
468
+ if contents['policy_groups']
469
+ contents['policy_groups'].each_pair do |group_name, group|
470
+ group['policies'].each do |policy_name, policy_revision|
471
+ data_store.set(['organizations', org_name, 'policy_groups', group_name, 'policies', policy_name], FFI_Yajl::Encoder.encode(policy_revision['revision_id'], :pretty => true), :create, :create_dir)
472
+ end
473
+ end
474
+ end
475
+
446
476
  if contents['cookbooks']
447
477
  contents['cookbooks'].each_pair do |name_version, cookbook|
448
478
  if name_version =~ /(.+)-(\d+\.\d+\.\d+)$/
@@ -481,7 +511,7 @@ module ChefZero
481
511
 
482
512
  private
483
513
 
484
- def open_source_endpoints
514
+ def endpoints
485
515
  result = if options[:osc_compat]
486
516
  # OSC-only
487
517
  [
@@ -525,11 +555,15 @@ module ChefZero
525
555
  end
526
556
  result + [
527
557
  # Both
558
+ [ "/dummy", DummyEndpoint.new(self) ],
528
559
  [ "/organizations/*/clients", ActorsEndpoint.new(self) ],
529
560
  [ "/organizations/*/clients/*", ActorEndpoint.new(self) ],
530
561
  [ "/organizations/*/cookbooks", CookbooksEndpoint.new(self) ],
531
562
  [ "/organizations/*/cookbooks/*", CookbookEndpoint.new(self) ],
532
563
  [ "/organizations/*/cookbooks/*/*", CookbookVersionEndpoint.new(self) ],
564
+ [ "/organizations/*/cookbook_artifacts", CookbookArtifactsEndpoint.new(self) ],
565
+ [ "/organizations/*/cookbook_artifacts/*", CookbookArtifactsCookbookEndpoint.new(self) ],
566
+ [ "/organizations/*/cookbook_artifacts/*/*", CookbookArtifactsCookbookIdentifierEndpoint.new(self) ],
533
567
  [ "/organizations/*/data", DataBagsEndpoint.new(self) ],
534
568
  [ "/organizations/*/data/*", DataBagEndpoint.new(self) ],
535
569
  [ "/organizations/*/data/*/*", DataBagItemEndpoint.new(self) ],
@@ -541,10 +575,16 @@ module ChefZero
541
575
  [ "/organizations/*/environments/*/nodes", EnvironmentNodesEndpoint.new(self) ],
542
576
  [ "/organizations/*/environments/*/recipes", EnvironmentRecipesEndpoint.new(self) ],
543
577
  [ "/organizations/*/environments/*/roles/*", EnvironmentRoleEndpoint.new(self) ],
544
- [ "/organizations/*/nodes", RestListEndpoint.new(self) ],
578
+ [ "/organizations/*/nodes", NodesEndpoint.new(self) ],
545
579
  [ "/organizations/*/nodes/*", NodeEndpoint.new(self) ],
546
580
  [ "/organizations/*/nodes/*/_identifiers", NodeIdentifiersEndpoint.new(self) ],
547
- [ "/organizations/*/policies/*/*", PoliciesEndpoint.new(self) ],
581
+ [ "/organizations/*/policies", PoliciesEndpoint.new(self) ],
582
+ [ "/organizations/*/policies/*", PolicyEndpoint.new(self) ],
583
+ [ "/organizations/*/policies/*/revisions", PolicyRevisionsEndpoint.new(self) ],
584
+ [ "/organizations/*/policies/*/revisions/*", PolicyRevisionEndpoint.new(self) ],
585
+ [ "/organizations/*/policy_groups", PolicyGroupsEndpoint.new(self) ],
586
+ [ "/organizations/*/policy_groups/*", PolicyGroupEndpoint.new(self) ],
587
+ [ "/organizations/*/policy_groups/*/policies/*", PolicyGroupPolicyEndpoint.new(self) ],
548
588
  [ "/organizations/*/principals/*", PrincipalEndpoint.new(self) ],
549
589
  [ "/organizations/*/roles", RestListEndpoint.new(self) ],
550
590
  [ "/organizations/*/roles/*", RoleEndpoint.new(self) ],
@@ -570,7 +610,7 @@ module ChefZero
570
610
 
571
611
  def app
572
612
  return @app if @app
573
- router = RestRouter.new(open_source_endpoints)
613
+ router = RestRouter.new(endpoints)
574
614
  router.not_found = NotFoundEndpoint.new
575
615
 
576
616
  if options[:single_org]