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.
- checksums.yaml +4 -4
- data/Gemfile +14 -0
- data/Rakefile +12 -0
- data/bin/chef-zero +5 -0
- data/chef-zero.gemspec +33 -0
- data/lib/chef_zero/chef_data/data_normalizer.rb +30 -11
- data/lib/chef_zero/chef_data/default_creator.rb +5 -2
- data/lib/chef_zero/endpoints/cookbook_artifacts_cookbook_endpoint.rb +24 -0
- data/lib/chef_zero/endpoints/cookbook_artifacts_cookbook_identifier.rb +67 -0
- data/lib/chef_zero/endpoints/cookbook_artifacts_endpoint.rb +34 -0
- data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +4 -4
- data/lib/chef_zero/endpoints/dummy_endpoint.rb +31 -0
- data/lib/chef_zero/endpoints/node_endpoint.rb +14 -0
- data/lib/chef_zero/endpoints/nodes_endpoint.rb +35 -0
- data/lib/chef_zero/endpoints/policies_endpoint.rb +14 -139
- data/lib/chef_zero/endpoints/policy_endpoint.rb +24 -0
- data/lib/chef_zero/endpoints/policy_group_endpoint.rb +46 -0
- data/lib/chef_zero/endpoints/policy_group_policy_endpoint.rb +84 -0
- data/lib/chef_zero/endpoints/policy_groups_endpoint.rb +38 -0
- data/lib/chef_zero/endpoints/policy_revision_endpoint.rb +23 -0
- data/lib/chef_zero/endpoints/policy_revisions_endpoint.rb +15 -0
- data/lib/chef_zero/endpoints/principal_endpoint.rb +4 -0
- data/lib/chef_zero/rest_base.rb +39 -4
- data/lib/chef_zero/rspec.rb +33 -0
- data/lib/chef_zero/server.rb +45 -5
- data/lib/chef_zero/version.rb +1 -1
- data/spec/run_oc_pedant.rb +68 -7
- data/spec/support/oc_pedant.rb +33 -13
- metadata +58 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef05ae4a7aef9de3c3a3da1ec5fbd5143a1a0457
|
4
|
+
data.tar.gz: 01e350ee9b6ff42d42550e8bafa07e1de7d1d629
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac2bff1a5f4518d05262d3660ad96709ec60f495706bd0700d4084cdc1a6e14cca17a458a69067bf5da9a14024c7222a17e0bb8de2a582205d9ebe0acbd560e6
|
7
|
+
data.tar.gz: 727e1693591ff8b7e7c03c3fd9b0e57868429fe9c1286b8b59cb65850d54b8d90cf54e6bc2a9c4353c1f1cff5081e7853400be21ef50ac1cd98b0bc2ff63a1cb
|
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
gemspec
|
3
|
+
|
4
|
+
gem 'rest-client', :github => 'chef/rest-client'
|
5
|
+
|
6
|
+
gem 'oc-chef-pedant', :github => 'chef/chef-server'
|
7
|
+
|
8
|
+
# gem 'oc-chef-pedant', :path => "../chef-server"
|
9
|
+
|
10
|
+
# bundler resolve failure on "rspec_junit_formatter"
|
11
|
+
# gem 'chef-pedant', :github => 'opscode/chef-pedant', :ref => "server-cli-option"
|
12
|
+
|
13
|
+
gem 'chef', :github => 'chef/chef'
|
14
|
+
# gem 'chef', :path => "../chef"
|
data/Rakefile
CHANGED
@@ -15,6 +15,18 @@ task :pedant do
|
|
15
15
|
require File.expand_path('spec/run_oc_pedant')
|
16
16
|
end
|
17
17
|
|
18
|
+
desc "run pedant with CHEF_FS set"
|
19
|
+
task :cheffs do
|
20
|
+
ENV['CHEF_FS'] = "yes"
|
21
|
+
require File.expand_path('spec/run_oc_pedant')
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "run pedant with FILE_STORE set"
|
25
|
+
task :filestore do
|
26
|
+
ENV['FILE_STORE'] = "yes"
|
27
|
+
require File.expand_path('spec/run_oc_pedant')
|
28
|
+
end
|
29
|
+
|
18
30
|
desc "run oc pedant"
|
19
31
|
task :oc_pedant do
|
20
32
|
require File.expand_path('spec/run_oc_pedant')
|
data/bin/chef-zero
CHANGED
@@ -55,6 +55,11 @@ OptionParser.new do |opts|
|
|
55
55
|
options[:log_file] = value
|
56
56
|
end
|
57
57
|
|
58
|
+
opts.on("--enterprise", "Whether to run in enterprise mode") do |value|
|
59
|
+
options[:single_org] = nil
|
60
|
+
options[:osc_compat] = false
|
61
|
+
end
|
62
|
+
|
58
63
|
opts.on("--multi-org", "Whether to run in multi-org mode") do |value|
|
59
64
|
options[:single_org] = nil
|
60
65
|
end
|
data/chef-zero.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/lib')
|
2
|
+
require 'chef_zero/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'chef-zero'
|
6
|
+
s.version = ChefZero::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.summary = 'Self-contained, easy-setup, fast-start in-memory Chef server for testing and solo setup purposes'
|
9
|
+
s.description = s.summary
|
10
|
+
s.author = 'John Keiser'
|
11
|
+
s.email = 'jkeiser@opscode.com'
|
12
|
+
s.homepage = 'http://www.opscode.com'
|
13
|
+
s.license = 'Apache 2.0'
|
14
|
+
|
15
|
+
s.add_dependency 'mixlib-log', '~> 1.3'
|
16
|
+
s.add_dependency 'hashie', '>= 2.0', '< 4.0'
|
17
|
+
s.add_dependency 'uuidtools', '~> 2.1'
|
18
|
+
s.add_dependency 'ffi-yajl', '~> 2.2'
|
19
|
+
s.add_dependency 'rack'
|
20
|
+
|
21
|
+
s.add_development_dependency 'pry'
|
22
|
+
s.add_development_dependency 'pry-byebug'
|
23
|
+
s.add_development_dependency 'pry-stack_explorer'
|
24
|
+
s.add_development_dependency 'rake'
|
25
|
+
s.add_development_dependency 'rspec'
|
26
|
+
s.add_development_dependency 'github_changelog_generator'
|
27
|
+
|
28
|
+
s.bindir = 'bin'
|
29
|
+
s.executables = ['chef-zero']
|
30
|
+
s.require_path = 'lib'
|
31
|
+
s.files = %w(LICENSE README.md Gemfile Rakefile) + Dir.glob('*.gemspec') +
|
32
|
+
Dir.glob('{lib,spec}/**/*', File::FNM_DOTMATCH).reject {|f| File.directory?(f) }
|
33
|
+
end
|
@@ -79,7 +79,8 @@ module ChefZero
|
|
79
79
|
data_bag_item
|
80
80
|
end
|
81
81
|
|
82
|
-
def self.normalize_cookbook(endpoint, org_prefix, cookbook, name, version, base_uri, method
|
82
|
+
def self.normalize_cookbook(endpoint, org_prefix, cookbook, name, version, base_uri, method,
|
83
|
+
is_cookbook_artifact=false)
|
83
84
|
# TODO I feel dirty
|
84
85
|
if method != 'PUT'
|
85
86
|
cookbook.each_pair do |key, value|
|
@@ -92,24 +93,28 @@ module ChefZero
|
|
92
93
|
end
|
93
94
|
end
|
94
95
|
cookbook['name'] ||= "#{name}-#{version}"
|
95
|
-
# TODO
|
96
|
-
|
97
|
-
cookbook['cookbook_name'] ||= name
|
96
|
+
# TODO it feels wrong, but the real chef server doesn't expand 'version', so we don't either.
|
97
|
+
|
98
98
|
cookbook['frozen?'] ||= false
|
99
99
|
cookbook['metadata'] ||= {}
|
100
100
|
cookbook['metadata']['version'] ||= version
|
101
|
-
|
102
|
-
|
103
|
-
|
101
|
+
|
102
|
+
# defaults set by the client and not the Server:
|
103
|
+
# metadata[name, description, maintainer, maintainer_email, license]
|
104
|
+
|
104
105
|
cookbook['metadata']['long_description'] ||= ""
|
105
|
-
# cookbook['metadata']['maintainer'] ||= "YOUR_COMPANY_NAME"
|
106
|
-
# cookbook['metadata']['maintainer_email'] ||= "YOUR_EMAIL"
|
107
|
-
# cookbook['metadata']['license'] ||= "none"
|
108
106
|
cookbook['metadata']['dependencies'] ||= {}
|
109
107
|
cookbook['metadata']['attributes'] ||= {}
|
110
108
|
cookbook['metadata']['recipes'] ||= {}
|
111
109
|
end
|
112
|
-
|
110
|
+
|
111
|
+
if is_cookbook_artifact
|
112
|
+
cookbook.delete('json_class')
|
113
|
+
else
|
114
|
+
cookbook['cookbook_name'] ||= name
|
115
|
+
cookbook['json_class'] ||= 'Chef::CookbookVersion'
|
116
|
+
end
|
117
|
+
|
113
118
|
cookbook['chef_type'] ||= 'cookbook_version'
|
114
119
|
if method == 'MIN'
|
115
120
|
cookbook['metadata'].delete('attributes')
|
@@ -166,6 +171,20 @@ module ChefZero
|
|
166
171
|
node
|
167
172
|
end
|
168
173
|
|
174
|
+
def self.normalize_policy(policy, name, revision)
|
175
|
+
policy['name'] ||= name
|
176
|
+
policy['revision_id'] ||= revision
|
177
|
+
policy['run_list'] ||= []
|
178
|
+
policy['cookbook_locks'] ||= {}
|
179
|
+
policy
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.normalize_policy_group(policy_group, name)
|
183
|
+
policy_group[name] ||= 'name'
|
184
|
+
policy_group['policies'] ||= {}
|
185
|
+
policy_group
|
186
|
+
end
|
187
|
+
|
169
188
|
def self.normalize_organization(org, name)
|
170
189
|
org['name'] ||= name
|
171
190
|
org['full_name'] ||= name
|
@@ -155,6 +155,8 @@ module ChefZero
|
|
155
155
|
'checksums' => {}
|
156
156
|
},
|
157
157
|
'nodes' => {},
|
158
|
+
'policies' => {},
|
159
|
+
'policy_groups' => {},
|
158
160
|
'roles' => {},
|
159
161
|
'sandboxes' => {},
|
160
162
|
'users' => {},
|
@@ -378,11 +380,12 @@ module ChefZero
|
|
378
380
|
# Non-default containers do not get superusers added to them,
|
379
381
|
# because reasons.
|
380
382
|
unless path.size == 4 && path[0] == 'organizations' && path[2] == 'containers' && !exists?(path)
|
381
|
-
owners
|
383
|
+
owners += superusers
|
382
384
|
end
|
383
385
|
end
|
384
386
|
|
385
|
-
|
387
|
+
# we don't de-dup this list, because pedant expects to see ["pivotal", "pivotal"] in some cases.
|
388
|
+
owners
|
386
389
|
end
|
387
390
|
|
388
391
|
def default_acl(acl_path, acl={})
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'chef_zero/chef_data/data_normalizer'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
module Endpoints
|
5
|
+
class CookbookArtifactsCookbookEndpoint < RestBase
|
6
|
+
# GET /organizations/ORG/cookbook_artifacts/COOKBOOK
|
7
|
+
def get(request)
|
8
|
+
cookbook_name = request.rest_path.last
|
9
|
+
cookbook_url = build_uri(request.base_uri, request.rest_path)
|
10
|
+
response_data = {}
|
11
|
+
versions = []
|
12
|
+
|
13
|
+
list_data(request).each do |identifier|
|
14
|
+
artifact_url = build_uri(request.base_uri, request.rest_path + [cookbook_name, identifier])
|
15
|
+
versions << { url: artifact_url, identifier: identifier }
|
16
|
+
end
|
17
|
+
|
18
|
+
response_data[cookbook_name] = { url: cookbook_url, versions: versions }
|
19
|
+
|
20
|
+
return json_response(200, response_data)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'chef_zero/chef_data/data_normalizer'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
module Endpoints
|
5
|
+
class CookbookArtifactsCookbookIdentifierEndpoint < ChefZero::Endpoints::CookbookVersionEndpoint
|
6
|
+
# these endpoints are almost, but not quite, not entirely unlike the corresponding /cookbooks endpoints.
|
7
|
+
# it could all be refactored for maximum reuse, but they're short REST methods with well-defined
|
8
|
+
# behavioral specs (pedant), so there's not a huge benefit.
|
9
|
+
|
10
|
+
# GET /organizations/ORG/cookbook_artifacts/NAME/IDENTIFIER
|
11
|
+
def get(request)
|
12
|
+
cookbook_data = normalize(request, parse_json(get_data(request)))
|
13
|
+
return json_response(200, cookbook_data)
|
14
|
+
end
|
15
|
+
|
16
|
+
# PUT /organizations/ORG/cookbook_artifacts/COOKBOOK/IDENTIFIER
|
17
|
+
def put(request)
|
18
|
+
if exists_data?(request)
|
19
|
+
return error(409, "Cookbooks cannot be modified, and a cookbook with this identifier already exists.")
|
20
|
+
end
|
21
|
+
|
22
|
+
set_data(request, nil, request.body, :create_dir)
|
23
|
+
|
24
|
+
return already_json_response(201, request.body)
|
25
|
+
end
|
26
|
+
|
27
|
+
# DELETE /organizations/ORG/cookbook_artifacts/COOKBOOK/IDENTIFIER
|
28
|
+
def delete(request)
|
29
|
+
begin
|
30
|
+
doomed_cookbook_json = get_data(request)
|
31
|
+
identified_cookbook_data = normalize(request, parse_json(doomed_cookbook_json))
|
32
|
+
delete_data(request)
|
33
|
+
|
34
|
+
# go through the recipes and delete stuff in the file store.
|
35
|
+
hoover_unused_checksums(get_checksums(doomed_cookbook_json), request, 'cookbook_artifacts')
|
36
|
+
|
37
|
+
# if this was the last revision, delete the directory so future requests will 404, instead of
|
38
|
+
# returning 200 with an empty list.
|
39
|
+
artifact_path = request.rest_path[0..-2]
|
40
|
+
if list_data(request, artifact_path).size == 0
|
41
|
+
delete_data_dir(request, artifact_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
json_response(200, identified_cookbook_data)
|
45
|
+
rescue RestErrorResponse => ex
|
46
|
+
if ex.response_code == 404
|
47
|
+
error(404, "not_found")
|
48
|
+
else
|
49
|
+
raise
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def make_file_store_path(rest_path, recipe)
|
57
|
+
rest_path.first(2) + ["file_store", "checksums", recipe["checksum"]]
|
58
|
+
end
|
59
|
+
|
60
|
+
def normalize(request, cookbook_artifact_data)
|
61
|
+
ChefData::DataNormalizer.normalize_cookbook(self, request.rest_path[0..1],
|
62
|
+
cookbook_artifact_data, request.rest_path[3], request.rest_path[4],
|
63
|
+
request.base_uri, request.method, true)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'chef_zero/chef_data/data_normalizer'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
module Endpoints
|
5
|
+
class CookbookArtifactsEndpoint < RestBase
|
6
|
+
# GET /organizations/ORG/cookbook_artifacts
|
7
|
+
def get(request)
|
8
|
+
data = {}
|
9
|
+
|
10
|
+
artifacts = begin
|
11
|
+
list_data(request)
|
12
|
+
rescue Exception => e
|
13
|
+
if e.response_code == 404
|
14
|
+
return already_json_response(200, "{}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
artifacts.each do |cookbook_artifact|
|
19
|
+
cookbook_url = build_uri(request.base_uri, request.rest_path + [cookbook_artifact])
|
20
|
+
|
21
|
+
versions = []
|
22
|
+
list_data(request, request.rest_path + [cookbook_artifact]).each do |identifier|
|
23
|
+
artifact_url = build_uri(request.base_uri, request.rest_path + [cookbook_artifact, identifier])
|
24
|
+
versions << { url: artifact_url, identifier: identifier }
|
25
|
+
end
|
26
|
+
|
27
|
+
data[cookbook_artifact] = { url: cookbook_url, versions: versions }
|
28
|
+
end
|
29
|
+
|
30
|
+
return json_response(200, data)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -85,10 +85,10 @@ module ChefZero
|
|
85
85
|
|
86
86
|
private
|
87
87
|
|
88
|
-
def hoover_unused_checksums(deleted_checksums, request)
|
89
|
-
data_store.list(request.rest_path[0..1] + [
|
90
|
-
data_store.list(request.rest_path[0..1] + [
|
91
|
-
cookbook = data_store.get(request.rest_path[0..1] + [
|
88
|
+
def hoover_unused_checksums(deleted_checksums, request, data_type='cookbooks')
|
89
|
+
data_store.list(request.rest_path[0..1] + [data_type]).each do |cookbook_name|
|
90
|
+
data_store.list(request.rest_path[0..1] + [data_type, cookbook_name]).each do |version|
|
91
|
+
cookbook = data_store.get(request.rest_path[0..1] + [data_type, cookbook_name, version], request)
|
92
92
|
deleted_checksums = deleted_checksums - get_checksums(cookbook)
|
93
93
|
end
|
94
94
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
# pedant makes a couple of Solr-related calls from its search_utils.rb file that we can't work around (e.g.
|
3
|
+
# with monkeypatching). the necessary Pedant::Config values are set in run_oc_pedant.rb. --cdoherty
|
4
|
+
module ChefZero
|
5
|
+
module Endpoints
|
6
|
+
class DummyEndpoint < RestBase
|
7
|
+
# called by #direct_solr_query, once each for roles, nodes, and data bag items. each RSpec example makes
|
8
|
+
# 3 calls, with the expected sequence of return values [0, 1, 0].
|
9
|
+
def get(request)
|
10
|
+
|
11
|
+
# this could be made less brittle, but if things change to have more than 3 cycles, we should really
|
12
|
+
# be notified by a spec failure.
|
13
|
+
@mock_values ||= ([0, 1, 0] * 3).map { |val| make_response(val) }
|
14
|
+
|
15
|
+
retval = @mock_values.shift
|
16
|
+
json_response(200, retval)
|
17
|
+
end
|
18
|
+
|
19
|
+
# called by #force_solr_commit in pedant's , which doesn't check the return value.
|
20
|
+
def post(request)
|
21
|
+
# sure thing!
|
22
|
+
json_response(200, { message: "This dummy POST endpoint didn't do anything." })
|
23
|
+
end
|
24
|
+
|
25
|
+
def make_response(value)
|
26
|
+
{ "response" => { "numFound" => value } }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -6,6 +6,20 @@ module ChefZero
|
|
6
6
|
module Endpoints
|
7
7
|
# /nodes/ID
|
8
8
|
class NodeEndpoint < RestObjectEndpoint
|
9
|
+
def put(request)
|
10
|
+
data = parse_json(request.body)
|
11
|
+
|
12
|
+
if data.has_key?("policy_name") && policy_name_invalid?(data["policy_name"])
|
13
|
+
return error(400, "Field 'policy_name' invalid", :pretty => false)
|
14
|
+
end
|
15
|
+
|
16
|
+
if data.has_key?("policy_group") && policy_name_invalid?(data["policy_group"])
|
17
|
+
return error(400, "Field 'policy_group' invalid", :pretty => false)
|
18
|
+
end
|
19
|
+
|
20
|
+
super(request)
|
21
|
+
end
|
22
|
+
|
9
23
|
def populate_defaults(request, response_json)
|
10
24
|
node = FFI_Yajl::Parser.parse(response_json, :create_additions => false)
|
11
25
|
node = ChefData::DataNormalizer.normalize_node(node, request.rest_path[3])
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'ffi_yajl'
|
2
|
+
require 'chef_zero/endpoints/rest_object_endpoint'
|
3
|
+
require 'chef_zero/chef_data/data_normalizer'
|
4
|
+
|
5
|
+
module ChefZero
|
6
|
+
module Endpoints
|
7
|
+
# /nodes
|
8
|
+
class NodesEndpoint < RestListEndpoint
|
9
|
+
|
10
|
+
def post(request)
|
11
|
+
# /nodes validation
|
12
|
+
if request.rest_path.last == "nodes"
|
13
|
+
data = parse_json(request.body)
|
14
|
+
|
15
|
+
if data.has_key?("policy_name") && policy_name_invalid?(data["policy_name"])
|
16
|
+
return error(400, "Field 'policy_name' invalid", :pretty => false)
|
17
|
+
end
|
18
|
+
|
19
|
+
if data.has_key?("policy_group") && policy_name_invalid?(data["policy_group"])
|
20
|
+
return error(400, "Field 'policy_group' invalid", :pretty => false)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
super(request)
|
25
|
+
end
|
26
|
+
|
27
|
+
def populate_defaults(request, response_json)
|
28
|
+
node = FFI_Yajl::Parser.parse(response_json, :create_additions => false)
|
29
|
+
node = ChefData::DataNormalizer.normalize_node(node, request.rest_path[3])
|
30
|
+
FFI_Yajl::Encoder.encode(node, :pretty => true)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -1,151 +1,26 @@
|
|
1
|
-
require 'ffi_yajl'
|
2
|
-
|
3
|
-
require 'chef_zero/endpoints/rest_object_endpoint'
|
4
1
|
require 'chef_zero/chef_data/data_normalizer'
|
5
2
|
|
6
3
|
module ChefZero
|
7
4
|
module Endpoints
|
8
|
-
# /policies
|
9
|
-
class PoliciesEndpoint <
|
10
|
-
|
11
|
-
super(server, 'id')
|
12
|
-
end
|
13
|
-
|
5
|
+
# /organizations/ORG/policies
|
6
|
+
class PoliciesEndpoint < RestBase
|
7
|
+
# GET /organizations/ORG/policies
|
14
8
|
def get(request)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
return error if error
|
22
|
-
|
23
|
-
code =
|
24
|
-
if data_store.exists?(request.rest_path)
|
25
|
-
set_data(request, request.rest_path, request.body, :data_store_exceptions)
|
26
|
-
200
|
27
|
-
else
|
28
|
-
name = request.rest_path[4]
|
29
|
-
data_store.create(request.rest_path[0..3], name, request.body, :create_dir)
|
30
|
-
201
|
31
|
-
end
|
32
|
-
already_json_response(code, request.body)
|
33
|
-
end
|
34
|
-
|
35
|
-
def delete(request)
|
36
|
-
result = get_data(request, request.rest_path)
|
37
|
-
delete_data(request, request.rest_path, :data_store_exceptions)
|
38
|
-
already_json_response(200, result)
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def validate(request)
|
44
|
-
req_object = validate_json(request.body)
|
45
|
-
validate_revision_id(request, req_object) ||
|
46
|
-
validate_name(request, req_object) ||
|
47
|
-
validate_run_list(req_object) ||
|
48
|
-
validate_each_run_list_item(req_object) ||
|
49
|
-
validate_cookbook_locks_collection(req_object) ||
|
50
|
-
validate_each_cookbook_locks_item(req_object)
|
51
|
-
end
|
52
|
-
|
53
|
-
def validate_json(request_body)
|
54
|
-
FFI_Yajl::Parser.parse(request_body)
|
55
|
-
# TODO: rescue parse error, return 400
|
56
|
-
# error(400, "Must specify #{identity_keys.map { |k| k.inspect }.join(' or ')} in JSON")
|
57
|
-
end
|
58
|
-
|
59
|
-
def validate_revision_id(request, req_object)
|
60
|
-
if !req_object.key?("revision_id")
|
61
|
-
error(400, "Field 'revision_id' missing")
|
62
|
-
elsif req_object["revision_id"].empty?
|
63
|
-
error(400, "Field 'revision_id' invalid")
|
64
|
-
elsif req_object["revision_id"].size > 255
|
65
|
-
error(400, "Field 'revision_id' invalid")
|
66
|
-
elsif req_object["revision_id"] !~ /^[\-[:alnum:]_\.\:]+$/
|
67
|
-
error(400, "Field 'revision_id' invalid")
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def validate_name(request, req_object)
|
72
|
-
if !req_object.key?("name")
|
73
|
-
error(400, "Field 'name' missing")
|
74
|
-
elsif req_object["name"] != (uri_policy_name = URI.decode(request.rest_path[4]))
|
75
|
-
error(400, "Field 'name' invalid : #{uri_policy_name} does not match #{req_object["name"]}")
|
76
|
-
elsif req_object["name"].size > 255
|
77
|
-
error(400, "Field 'name' invalid")
|
78
|
-
elsif req_object["name"] !~ /^[\-[:alnum:]_\.\:]+$/
|
79
|
-
error(400, "Field 'name' invalid")
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def validate_run_list(req_object)
|
84
|
-
if !req_object.key?("run_list")
|
85
|
-
error(400, "Field 'run_list' missing")
|
86
|
-
elsif !req_object["run_list"].kind_of?(Array)
|
87
|
-
error(400, "Field 'run_list' is not a valid run list")
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def validate_each_run_list_item(req_object)
|
92
|
-
req_object["run_list"].each do |run_list_item|
|
93
|
-
if res_400 = validate_run_list_item(run_list_item)
|
94
|
-
return res_400
|
95
|
-
end
|
96
|
-
end
|
97
|
-
nil
|
98
|
-
end
|
9
|
+
response_data = {}
|
10
|
+
policy_names = list_data(request)
|
11
|
+
policy_names.each do |policy_name|
|
12
|
+
policy_path = request.rest_path + [policy_name]
|
13
|
+
policy_uri = build_uri(request.base_uri, policy_path)
|
14
|
+
revisions = list_data(request, policy_path + ["revisions"])
|
99
15
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
error(400, "Field 'run_list' is not a valid run list")
|
16
|
+
response_data[policy_name] = {
|
17
|
+
uri: policy_uri,
|
18
|
+
revisions: hashify_list(revisions)
|
19
|
+
}
|
105
20
|
end
|
106
|
-
end
|
107
21
|
|
108
|
-
|
109
|
-
if !req_object.key?("cookbook_locks")
|
110
|
-
error(400, "Field 'cookbook_locks' missing")
|
111
|
-
elsif !req_object["cookbook_locks"].kind_of?(Hash)
|
112
|
-
error(400, "Field 'cookbook_locks' invalid")
|
113
|
-
end
|
22
|
+
return json_response(200, response_data)
|
114
23
|
end
|
115
|
-
|
116
|
-
def validate_each_cookbook_locks_item(req_object)
|
117
|
-
req_object["cookbook_locks"].each do |cookbook_name, lock|
|
118
|
-
if res_400 = validate_cookbook_locks_item(cookbook_name, lock)
|
119
|
-
return res_400
|
120
|
-
end
|
121
|
-
end
|
122
|
-
nil
|
123
|
-
end
|
124
|
-
|
125
|
-
def validate_cookbook_locks_item(cookbook_name, lock)
|
126
|
-
if !lock.kind_of?(Hash)
|
127
|
-
error(400, "cookbook_lock entries must be a JSON object")
|
128
|
-
elsif !lock.key?("identifier")
|
129
|
-
error(400, "Field 'identifier' missing")
|
130
|
-
elsif lock["identifier"].size > 255
|
131
|
-
error(400, "Field 'identifier' invalid")
|
132
|
-
elsif !lock.key?("version")
|
133
|
-
error(400, "Field 'version' missing")
|
134
|
-
elsif lock.key?("dotted_decimal_identifier")
|
135
|
-
unless valid_version?(lock["dotted_decimal_identifier"])
|
136
|
-
error(400, "Field 'dotted_decimal_identifier' is not a valid version")
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def valid_version?(version_string)
|
142
|
-
Gem::Version.new(version_string)
|
143
|
-
true
|
144
|
-
rescue ArgumentError
|
145
|
-
false
|
146
|
-
end
|
147
|
-
|
148
24
|
end
|
149
25
|
end
|
150
26
|
end
|
151
|
-
|