chef-zero 4.3.2 → 4.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|