chef-zero 4.5.0 → 4.6.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 +5 -1
- data/Rakefile +33 -19
- data/bin/chef-zero +2 -1
- data/chef-zero.gemspec +2 -1
- data/lib/chef_zero/chef_data/cookbook_data.rb +5 -0
- data/lib/chef_zero/chef_data/data_normalizer.rb +3 -3
- data/lib/chef_zero/data_store/data_error.rb +4 -3
- data/lib/chef_zero/endpoints/actor_default_key_endpoint.rb +77 -0
- data/lib/chef_zero/endpoints/actor_endpoint.rb +108 -19
- data/lib/chef_zero/endpoints/actor_key_endpoint.rb +62 -0
- data/lib/chef_zero/endpoints/actor_keys_endpoint.rb +129 -0
- data/lib/chef_zero/endpoints/actors_endpoint.rb +38 -12
- data/lib/chef_zero/endpoints/organization_user_default_key_endpoint.rb +16 -0
- data/lib/chef_zero/endpoints/organization_user_key_endpoint.rb +17 -0
- data/lib/chef_zero/endpoints/organization_user_keys_endpoint.rb +17 -0
- data/lib/chef_zero/endpoints/principal_endpoint.rb +11 -2
- data/lib/chef_zero/endpoints/rest_object_endpoint.rb +19 -5
- data/lib/chef_zero/endpoints/server_api_version_endpoint.rb +1 -1
- data/lib/chef_zero/rest_base.rb +75 -32
- data/lib/chef_zero/rest_error_response.rb +3 -3
- data/lib/chef_zero/rest_request.rb +12 -1
- data/lib/chef_zero/rest_router.rb +44 -17
- data/lib/chef_zero/rspec.rb +1 -1
- data/lib/chef_zero/server.rb +46 -23
- data/lib/chef_zero/version.rb +1 -1
- data/spec/run_oc_pedant.rb +76 -33
- metadata +9 -17
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'chef_zero/rest_base'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
module Endpoints
|
5
|
+
# ActorKeyEndpoint
|
6
|
+
#
|
7
|
+
# This class handles DELETE/GET/PUT requests for all client/user keys
|
8
|
+
# **except** default public keys, i.e. requests with identity key
|
9
|
+
# "default". Those are handled by ActorDefaultKeyEndpoint. See that class
|
10
|
+
# for more information.
|
11
|
+
#
|
12
|
+
# /users/USER/keys/NAME
|
13
|
+
# /organizations/ORG/clients/CLIENT/keys/NAME
|
14
|
+
class ActorKeyEndpoint < RestBase
|
15
|
+
def get(request)
|
16
|
+
validate_actor!(request)
|
17
|
+
key_path = data_path(request)
|
18
|
+
already_json_response(200, get_data(request, key_path))
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete(request)
|
22
|
+
validate_actor!(request) # 404 if actor doesn't exist
|
23
|
+
|
24
|
+
key_path = data_path(request)
|
25
|
+
data = get_data(request, key_path)
|
26
|
+
delete_data(request, key_path)
|
27
|
+
|
28
|
+
already_json_response(200, data)
|
29
|
+
end
|
30
|
+
|
31
|
+
def put(request)
|
32
|
+
validate_actor!(request) # 404 if actor doesn't exist
|
33
|
+
set_data(request, data_path(request), request.body)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Returns the keys data store path, which is the same as
|
39
|
+
# `request.rest_path` except with "client_keys" instead of "clients" or
|
40
|
+
# "user_keys" instead of "users."
|
41
|
+
def data_path(request)
|
42
|
+
request.rest_path.dup.tap do |path|
|
43
|
+
if client?(request)
|
44
|
+
path[2] = "client_keys"
|
45
|
+
else
|
46
|
+
path[0] = "user_keys"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Raises RestErrorResponse (404) if actor doesn't exist
|
52
|
+
def validate_actor!(request)
|
53
|
+
actor_path = request.rest_path[ client?(request) ? 0..3 : 0..1 ]
|
54
|
+
get_data(request, actor_path)
|
55
|
+
end
|
56
|
+
|
57
|
+
def client?(request)
|
58
|
+
request.rest_path[2] == "clients"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'chef_zero/rest_base'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
module Endpoints
|
5
|
+
# /users/USER/keys
|
6
|
+
# /organizations/ORG/clients/CLIENT/keys
|
7
|
+
class ActorKeysEndpoint < RestBase
|
8
|
+
DEFAULT_PUBLIC_KEY_NAME = "default"
|
9
|
+
DATE_FORMAT = "%FT%TZ" # e.g. 2015-12-24T21:00:00Z
|
10
|
+
|
11
|
+
def get(request, alt_uri_root=nil)
|
12
|
+
path = data_path(request)
|
13
|
+
|
14
|
+
# Get actor or 404 if it doesn't exist
|
15
|
+
actor_json = get_data(request, actor_path(request))
|
16
|
+
|
17
|
+
key_names = list_data_or_else(request, path, [])
|
18
|
+
key_names.unshift(DEFAULT_PUBLIC_KEY_NAME) if actor_has_default_public_key?(actor_json)
|
19
|
+
|
20
|
+
result = key_names.map do |key_name|
|
21
|
+
list_key(request, [ *path, key_name ], alt_uri_root)
|
22
|
+
end
|
23
|
+
|
24
|
+
json_response(200, result)
|
25
|
+
end
|
26
|
+
|
27
|
+
def post(request)
|
28
|
+
request_body = parse_json(request.body)
|
29
|
+
|
30
|
+
# Try loading the client or user so a 404 is returned if it doesn't exist
|
31
|
+
actor_json = get_data(request, actor_path(request))
|
32
|
+
|
33
|
+
generate_keys = request_body["public_key"].nil?
|
34
|
+
|
35
|
+
if generate_keys
|
36
|
+
private_key, public_key = server.gen_key_pair
|
37
|
+
else
|
38
|
+
public_key = request_body['public_key']
|
39
|
+
end
|
40
|
+
|
41
|
+
key_name = request_body["name"]
|
42
|
+
|
43
|
+
if key_name == DEFAULT_PUBLIC_KEY_NAME
|
44
|
+
store_actor_default_public_key!(request, actor_json, public_key)
|
45
|
+
else
|
46
|
+
store_actor_public_key!(request, key_name, public_key, request_body["expiration_date"])
|
47
|
+
end
|
48
|
+
|
49
|
+
response_body = { "uri" => key_uri(request, key_name) }
|
50
|
+
response_body["private_key"] = private_key if generate_keys
|
51
|
+
|
52
|
+
json_response(201, response_body,
|
53
|
+
headers: { "Location" => response_body["uri"] })
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def store_actor_public_key!(request, name, public_key, expiration_date)
|
59
|
+
data = to_json(
|
60
|
+
"name" => name,
|
61
|
+
"public_key" => public_key,
|
62
|
+
"expiration_date" => expiration_date
|
63
|
+
)
|
64
|
+
|
65
|
+
create_data(request, data_path(request), name, data, :create_dir)
|
66
|
+
end
|
67
|
+
|
68
|
+
def store_actor_default_public_key!(request, actor_json, public_key)
|
69
|
+
actor_data = parse_json(actor_json)
|
70
|
+
|
71
|
+
if actor_data["public_key"]
|
72
|
+
raise RestErrorResponse.new(409, "Object already exists: #{key_uri(request, DEFAULT_PUBLIC_KEY_NAME)}")
|
73
|
+
end
|
74
|
+
|
75
|
+
actor_data["public_key"] = public_key
|
76
|
+
set_data(request, actor_path(request), to_json(actor_data))
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the keys data store path, which is the same as
|
81
|
+
# `request.rest_path` except with "user_keys" instead of "users" or
|
82
|
+
# "client_keys" instead of "clients."
|
83
|
+
def data_path(request)
|
84
|
+
request.rest_path.dup.tap do |path|
|
85
|
+
if client?(request)
|
86
|
+
path[2] = "client_keys"
|
87
|
+
else
|
88
|
+
path[0] = "user_keys"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def list_key(request, data_path, alt_uri_root=nil)
|
94
|
+
key_name, expiration_date =
|
95
|
+
if data_path[-1] == DEFAULT_PUBLIC_KEY_NAME
|
96
|
+
[ DEFAULT_PUBLIC_KEY_NAME, "infinity" ]
|
97
|
+
else
|
98
|
+
parse_json(get_data(request, data_path))
|
99
|
+
.values_at("name", "expiration_date")
|
100
|
+
end
|
101
|
+
|
102
|
+
expired = expiration_date != "infinity" &&
|
103
|
+
DateTime.now > DateTime.strptime(expiration_date, DATE_FORMAT)
|
104
|
+
|
105
|
+
{ "name" => key_name,
|
106
|
+
"uri" => key_uri(request, key_name, alt_uri_root),
|
107
|
+
"expired" => expired }
|
108
|
+
end
|
109
|
+
|
110
|
+
def client?(request)
|
111
|
+
request.rest_path[2] == "clients"
|
112
|
+
end
|
113
|
+
|
114
|
+
def key_uri(request, key_name, alt_uri_root=nil)
|
115
|
+
uri_root = alt_uri_root.nil? ? request.rest_path : alt_uri_root
|
116
|
+
build_uri(request.base_uri, [ *uri_root, key_name ])
|
117
|
+
end
|
118
|
+
|
119
|
+
def actor_path(request)
|
120
|
+
return request.rest_path[0..3] if client?(request)
|
121
|
+
request.rest_path[0..1]
|
122
|
+
end
|
123
|
+
|
124
|
+
def actor_has_default_public_key?(actor_json)
|
125
|
+
!!parse_json(actor_json)["public_key"]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -9,29 +9,29 @@ module ChefZero
|
|
9
9
|
response = super(request)
|
10
10
|
|
11
11
|
if request.query_params['email']
|
12
|
-
results =
|
12
|
+
results = parse_json(response[2])
|
13
13
|
new_results = {}
|
14
14
|
results.each do |name, url|
|
15
15
|
record = get_data(request, request.rest_path + [ name ], :nil)
|
16
16
|
if record
|
17
|
-
record =
|
17
|
+
record = parse_json(record)
|
18
18
|
new_results[name] = url if record['email'] == request.query_params['email']
|
19
19
|
end
|
20
20
|
end
|
21
|
-
response[2] =
|
21
|
+
response[2] = to_json(new_results)
|
22
22
|
end
|
23
23
|
|
24
24
|
if request.query_params['verbose']
|
25
|
-
results =
|
25
|
+
results = parse_json(response[2])
|
26
26
|
results.each do |name, url|
|
27
27
|
record = get_data(request, request.rest_path + [ name ], :nil)
|
28
28
|
if record
|
29
|
-
record =
|
29
|
+
record = parse_json(record)
|
30
30
|
record = ChefData::DataNormalizer.normalize_user(record, name, identity_keys, server.options[:osc_compat])
|
31
31
|
results[name] = record
|
32
32
|
end
|
33
33
|
end
|
34
|
-
response[2] =
|
34
|
+
response[2] = to_json(results)
|
35
35
|
end
|
36
36
|
response
|
37
37
|
end
|
@@ -39,21 +39,47 @@ module ChefZero
|
|
39
39
|
def post(request)
|
40
40
|
# First, find out if the user actually posted a public key. If not, make
|
41
41
|
# one.
|
42
|
-
request_body =
|
42
|
+
request_body = parse_json(request.body)
|
43
43
|
public_key = request_body['public_key']
|
44
|
-
|
44
|
+
|
45
|
+
skip_key_create = !request.api_v0? && !request_body["create_key"]
|
46
|
+
|
47
|
+
if !public_key && !skip_key_create
|
45
48
|
private_key, public_key = server.gen_key_pair
|
46
49
|
request_body['public_key'] = public_key
|
47
|
-
request.body =
|
50
|
+
request.body = to_json(request_body)
|
51
|
+
elsif skip_key_create
|
52
|
+
request_body['public_key'] = nil
|
53
|
+
request.body = to_json(request_body)
|
48
54
|
end
|
49
55
|
|
50
56
|
result = super(request)
|
51
57
|
|
52
58
|
if result[0] == 201
|
53
59
|
# If we generated a key, stuff it in the response.
|
54
|
-
|
55
|
-
|
56
|
-
|
60
|
+
user_data = parse_json(result[2])
|
61
|
+
|
62
|
+
key_data = {}
|
63
|
+
key_data['private_key'] = private_key if private_key
|
64
|
+
key_data['public_key'] = public_key unless request.rest_path[0] == 'users'
|
65
|
+
|
66
|
+
response =
|
67
|
+
if request.api_v0?
|
68
|
+
user_data.merge(key_data)
|
69
|
+
elsif skip_key_create && !public_key
|
70
|
+
user_data
|
71
|
+
else
|
72
|
+
actor_name = request_body["name"] || request_body["username"] || request_body["clientname"]
|
73
|
+
|
74
|
+
relpath_to_default_key = [ actor_name, "keys", "default" ]
|
75
|
+
key_data["uri"] = build_uri(request.base_uri, request.rest_path + relpath_to_default_key)
|
76
|
+
key_data["public_key"] = public_key
|
77
|
+
key_data["name"] = "default"
|
78
|
+
key_data["expiration_date"] = "infinity"
|
79
|
+
user_data["chef_key"] = key_data
|
80
|
+
user_data
|
81
|
+
end
|
82
|
+
|
57
83
|
json_response(201, response)
|
58
84
|
else
|
59
85
|
result
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'chef_zero/rest_base'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
module Endpoints
|
5
|
+
# GET /organizations/ORG/users/USER/keys/default
|
6
|
+
class OrganizationUserDefaultKeyEndpoint < RestBase
|
7
|
+
def get(request)
|
8
|
+
# 404 if it doesn't exist
|
9
|
+
get_data(request, request.rest_path[0..3])
|
10
|
+
# Just use the /users/USER/keys/default endpoint
|
11
|
+
request.rest_path = request.rest_path[2..-1]
|
12
|
+
ActorDefaultKeyEndpoint.new(server).get(request)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'chef_zero/rest_base'
|
2
|
+
require 'chef_zero/endpoints/actor_keys_endpoint'
|
3
|
+
|
4
|
+
module ChefZero
|
5
|
+
module Endpoints
|
6
|
+
# GET /organizations/ORG/users/USER/keys/NAME
|
7
|
+
class OrganizationUserKeyEndpoint < RestBase
|
8
|
+
def get(request)
|
9
|
+
# 404 if not a member of the org
|
10
|
+
get_data(request, request.rest_path[0..3])
|
11
|
+
# Just use the /users/USER/keys endpoint
|
12
|
+
request.rest_path = request.rest_path[2..-1]
|
13
|
+
ActorKeyEndpoint.new(server).get(request)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'chef_zero/rest_base'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
module Endpoints
|
5
|
+
# GET /organizations/ORG/users/USER/keys
|
6
|
+
class OrganizationUserKeysEndpoint < RestBase
|
7
|
+
def get(request)
|
8
|
+
# 404 if it doesn't exist
|
9
|
+
get_data(request, request.rest_path[0..3])
|
10
|
+
# Just use the /users/USER/keys/key endpoint
|
11
|
+
original_path = request.rest_path
|
12
|
+
request.rest_path = request.rest_path[2..-1]
|
13
|
+
ActorKeysEndpoint.new(server).get(request, original_path)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -30,13 +30,22 @@ module ChefZero
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
if json
|
33
|
-
|
33
|
+
principal_data = {
|
34
34
|
'name' => name,
|
35
35
|
'type' => type,
|
36
36
|
'public_key' => FFI_Yajl::Parser.parse(json)['public_key'] || PUBLIC_KEY,
|
37
37
|
'authz_id' => '0'*32,
|
38
38
|
'org_member' => org_member
|
39
|
-
}
|
39
|
+
}
|
40
|
+
|
41
|
+
response_data =
|
42
|
+
if request.api_v0?
|
43
|
+
principal_data
|
44
|
+
else
|
45
|
+
{ "principals" => [ principal_data ] }
|
46
|
+
end
|
47
|
+
|
48
|
+
json_response(200, response_data)
|
40
49
|
else
|
41
50
|
error(404, 'Principal not found')
|
42
51
|
end
|
@@ -21,12 +21,11 @@ module ChefZero
|
|
21
21
|
def put(request)
|
22
22
|
# We grab the old body to trigger a 404 if it doesn't exist
|
23
23
|
old_body = get_data(request)
|
24
|
-
|
25
|
-
key = identity_keys.map { |k| request_json[k] }.select { |v| v }.first
|
26
|
-
key ||= request.rest_path[-1]
|
24
|
+
|
27
25
|
# If it's a rename, check for conflict and delete the old value
|
28
|
-
|
29
|
-
|
26
|
+
if is_rename?(request)
|
27
|
+
key = identity_key_value(request)
|
28
|
+
|
30
29
|
begin
|
31
30
|
create_data(request, request.rest_path[0..-2], key, request.body, :data_store_exceptions)
|
32
31
|
rescue DataStore::DataAlreadyExistsError
|
@@ -56,8 +55,23 @@ module ChefZero
|
|
56
55
|
return FFI_Yajl::Encoder.encode(merged_json, :pretty => true)
|
57
56
|
end
|
58
57
|
end
|
58
|
+
|
59
59
|
request.body
|
60
60
|
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Get the value of the (first existing) identity key from the request body or nil
|
65
|
+
def identity_key_value(request)
|
66
|
+
request_json = parse_json(request.body)
|
67
|
+
identity_keys.map { |k| request_json[k] }.compact.first
|
68
|
+
end
|
69
|
+
|
70
|
+
# Does this request change the value of the identity key?
|
71
|
+
def is_rename?(request)
|
72
|
+
return false unless key = identity_key_value(request)
|
73
|
+
key != request.rest_path[-1]
|
74
|
+
end
|
61
75
|
end
|
62
76
|
end
|
63
77
|
end
|
@@ -7,7 +7,7 @@ module ChefZero
|
|
7
7
|
API_VERSION = 1
|
8
8
|
def get(request)
|
9
9
|
json_response(200, {"min_api_version"=>MIN_API_VERSION, "max_api_version"=>MAX_API_VERSION},
|
10
|
-
request.api_version, API_VERSION)
|
10
|
+
request_version: request.api_version, response_version: API_VERSION)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
data/lib/chef_zero/rest_base.rb
CHANGED
@@ -5,6 +5,9 @@ require 'chef_zero/chef_data/acl_path'
|
|
5
5
|
|
6
6
|
module ChefZero
|
7
7
|
class RestBase
|
8
|
+
DEFAULT_REQUEST_VERSION = 0
|
9
|
+
DEFAULT_RESPONSE_VERSION = 0
|
10
|
+
|
8
11
|
def initialize(server)
|
9
12
|
@server = server
|
10
13
|
end
|
@@ -16,21 +19,28 @@ module ChefZero
|
|
16
19
|
end
|
17
20
|
|
18
21
|
def check_api_version(request)
|
19
|
-
|
20
|
-
|
22
|
+
return if request.api_version.nil? # Not present in headers
|
23
|
+
version = request.api_version.to_i
|
24
|
+
|
25
|
+
unless version.to_s == request.api_version.to_s # Version is not an Integer
|
26
|
+
return json_response(406,
|
27
|
+
{ "username" => request.requestor },
|
28
|
+
request_version: -1, response_version: -1
|
29
|
+
)
|
30
|
+
end
|
21
31
|
|
22
|
-
if version
|
23
|
-
return json_response(406, { "username" => request.requestor }, -1, -1)
|
24
|
-
elsif version.to_i > MAX_API_VERSION or version.to_i < MIN_API_VERSION
|
32
|
+
if version > MAX_API_VERSION || version < MIN_API_VERSION
|
25
33
|
response = {
|
26
34
|
"error" => "invalid-x-ops-server-api-version",
|
27
35
|
"message" => "Specified version #{version} not supported",
|
28
36
|
"min_api_version" => MIN_API_VERSION,
|
29
37
|
"max_api_version" => MAX_API_VERSION
|
30
38
|
}
|
31
|
-
|
32
|
-
|
33
|
-
|
39
|
+
|
40
|
+
return json_response(406,
|
41
|
+
response,
|
42
|
+
request_version: version, response_version: -1
|
43
|
+
)
|
34
44
|
end
|
35
45
|
end
|
36
46
|
|
@@ -51,7 +61,7 @@ module ChefZero
|
|
51
61
|
begin
|
52
62
|
self.send(method, request)
|
53
63
|
rescue RestErrorResponse => e
|
54
|
-
ChefZero::Log.
|
64
|
+
ChefZero::Log.info("#{e.inspect}\n#{e.backtrace.join("\n")}")
|
55
65
|
error(e.response_code, e.error)
|
56
66
|
end
|
57
67
|
end
|
@@ -104,7 +114,7 @@ module ChefZero
|
|
104
114
|
if options.include?(:data_store_exceptions)
|
105
115
|
raise
|
106
116
|
else
|
107
|
-
raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri,
|
117
|
+
raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}")
|
108
118
|
end
|
109
119
|
end
|
110
120
|
|
@@ -123,7 +133,7 @@ module ChefZero
|
|
123
133
|
if options.include?(:data_store_exceptions)
|
124
134
|
raise
|
125
135
|
else
|
126
|
-
raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri,
|
136
|
+
raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}")
|
127
137
|
end
|
128
138
|
end
|
129
139
|
|
@@ -142,7 +152,7 @@ module ChefZero
|
|
142
152
|
if options.include?(:data_store_exceptions)
|
143
153
|
raise
|
144
154
|
else
|
145
|
-
raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri,
|
155
|
+
raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}")
|
146
156
|
end
|
147
157
|
end
|
148
158
|
end
|
@@ -155,13 +165,13 @@ module ChefZero
|
|
155
165
|
if options.include?(:data_store_exceptions)
|
156
166
|
raise
|
157
167
|
else
|
158
|
-
raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri,
|
168
|
+
raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri, rest_path)}")
|
159
169
|
end
|
160
170
|
rescue DataStore::DataAlreadyExistsError
|
161
171
|
if options.include?(:data_store_exceptions)
|
162
172
|
raise
|
163
173
|
else
|
164
|
-
raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri,
|
174
|
+
raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri, rest_path + [name])}")
|
165
175
|
end
|
166
176
|
end
|
167
177
|
end
|
@@ -174,13 +184,13 @@ module ChefZero
|
|
174
184
|
if options.include?(:data_store_exceptions)
|
175
185
|
raise
|
176
186
|
else
|
177
|
-
raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri,
|
187
|
+
raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri, rest_path)}")
|
178
188
|
end
|
179
189
|
rescue DataStore::DataAlreadyExistsError
|
180
190
|
if options.include?(:data_store_exceptions)
|
181
191
|
raise
|
182
192
|
else
|
183
|
-
raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri,
|
193
|
+
raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri, rest_path + [name])}")
|
184
194
|
end
|
185
195
|
end
|
186
196
|
end
|
@@ -196,26 +206,59 @@ module ChefZero
|
|
196
206
|
end
|
197
207
|
|
198
208
|
def error(response_code, error, opts={})
|
199
|
-
json_response(response_code, {"error" => [error]},
|
209
|
+
json_response(response_code, { "error" => [ error ] }, opts)
|
200
210
|
end
|
201
211
|
|
202
|
-
|
203
|
-
|
204
|
-
|
212
|
+
# Serializes `data` to JSON and returns an Array with the
|
213
|
+
# response code, HTTP headers and JSON body.
|
214
|
+
#
|
215
|
+
# @param [Fixnum] response_code HTTP response code
|
216
|
+
# @param [Hash] data The data for the response body as a Hash
|
217
|
+
# @param [Hash] options
|
218
|
+
# @option options [Hash] :headers (see #already_json_response)
|
219
|
+
# @option options [Boolean] :pretty (true) Pretty-format the JSON
|
220
|
+
# @option options [Fixnum] :request_version (see #already_json_response)
|
221
|
+
# @option options [Fixnum] :response_version (see #already_json_response)
|
222
|
+
#
|
223
|
+
# @return (see #already_json_response)
|
224
|
+
#
|
225
|
+
def json_response(response_code, data, options={})
|
226
|
+
options = { pretty: true }.merge(options)
|
227
|
+
do_pretty_json = !!options.delete(:pretty) # make sure we have a proper Boolean.
|
228
|
+
json = FFI_Yajl::Encoder.encode(data, pretty: do_pretty_json)
|
229
|
+
already_json_response(response_code, json, options)
|
205
230
|
end
|
206
231
|
|
207
232
|
def text_response(response_code, text)
|
208
233
|
[response_code, {"Content-Type" => "text/plain"}, text]
|
209
234
|
end
|
210
235
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
236
|
+
# Returns an Array with the response code, HTTP headers, and JSON body.
|
237
|
+
#
|
238
|
+
# @param [Fixnum] response_code The HTTP response code
|
239
|
+
# @param [String] json_text The JSON body for the response
|
240
|
+
# @param [Hash] options
|
241
|
+
# @option options [Hash] :headers ({}) HTTP headers (may override default headers)
|
242
|
+
# @option options [Fixnum] :request_version (0) Request API version
|
243
|
+
# @option options [Fixnum] :response_version (0) Response API version
|
244
|
+
#
|
245
|
+
# @return [Array(Fixnum, Hash{String => String}, String)]
|
246
|
+
#
|
247
|
+
def already_json_response(response_code, json_text, options={})
|
248
|
+
version_header = FFI_Yajl::Encoder.encode(
|
249
|
+
"min_version" => MIN_API_VERSION.to_s,
|
250
|
+
"max_version" => MAX_API_VERSION.to_s,
|
251
|
+
"request_version" => options[:request_version] || DEFAULT_REQUEST_VERSION.to_s,
|
252
|
+
"response_version" => options[:response_version] || DEFAULT_RESPONSE_VERSION.to_s
|
253
|
+
)
|
254
|
+
|
255
|
+
headers = {
|
256
|
+
"Content-Type" => "application/json",
|
257
|
+
"X-Ops-Server-API-Version" => version_header
|
258
|
+
}
|
259
|
+
headers.merge!(options[:headers]) if options[:headers]
|
260
|
+
|
261
|
+
[ response_code, headers, json_text ]
|
219
262
|
end
|
220
263
|
|
221
264
|
# To be called from inside rest endpoints
|
@@ -224,12 +267,12 @@ module ChefZero
|
|
224
267
|
# Strip off /organizations/chef if we are in single org mode
|
225
268
|
if rest_path[0..1] != [ 'organizations', server.options[:single_org] ]
|
226
269
|
raise "Unexpected URL #{rest_path[0..1]} passed to build_uri in single org mode"
|
227
|
-
else
|
228
|
-
"#{base_uri}/#{rest_path[2..-1].join('/')}"
|
229
270
|
end
|
230
|
-
|
231
|
-
|
271
|
+
|
272
|
+
return self.class.build_uri(base_uri, rest_path[2..-1])
|
232
273
|
end
|
274
|
+
|
275
|
+
self.class.build_uri(base_uri, rest_path)
|
233
276
|
end
|
234
277
|
|
235
278
|
def self.build_uri(base_uri, rest_path)
|