chef-zero 1.4.0.alpha-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/LICENSE +201 -0
  2. data/README.md +145 -0
  3. data/Rakefile +11 -0
  4. data/bin/chef-zero +43 -0
  5. data/lib/chef_zero.rb +7 -0
  6. data/lib/chef_zero/cookbook_data.rb +223 -0
  7. data/lib/chef_zero/data_normalizer.rb +142 -0
  8. data/lib/chef_zero/data_store/data_already_exists_error.rb +29 -0
  9. data/lib/chef_zero/data_store/data_error.rb +31 -0
  10. data/lib/chef_zero/data_store/data_not_found_error.rb +29 -0
  11. data/lib/chef_zero/data_store/memory_store.rb +167 -0
  12. data/lib/chef_zero/endpoints/actor_endpoint.rb +68 -0
  13. data/lib/chef_zero/endpoints/actors_endpoint.rb +32 -0
  14. data/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +25 -0
  15. data/lib/chef_zero/endpoints/cookbook_endpoint.rb +39 -0
  16. data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +110 -0
  17. data/lib/chef_zero/endpoints/cookbooks_base.rb +65 -0
  18. data/lib/chef_zero/endpoints/cookbooks_endpoint.rb +19 -0
  19. data/lib/chef_zero/endpoints/data_bag_endpoint.rb +45 -0
  20. data/lib/chef_zero/endpoints/data_bag_item_endpoint.rb +25 -0
  21. data/lib/chef_zero/endpoints/data_bags_endpoint.rb +22 -0
  22. data/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +24 -0
  23. data/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +109 -0
  24. data/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +22 -0
  25. data/lib/chef_zero/endpoints/environment_endpoint.rb +33 -0
  26. data/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +23 -0
  27. data/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +22 -0
  28. data/lib/chef_zero/endpoints/environment_role_endpoint.rb +36 -0
  29. data/lib/chef_zero/endpoints/file_store_file_endpoint.rb +22 -0
  30. data/lib/chef_zero/endpoints/node_endpoint.rb +17 -0
  31. data/lib/chef_zero/endpoints/not_found_endpoint.rb +11 -0
  32. data/lib/chef_zero/endpoints/principal_endpoint.rb +30 -0
  33. data/lib/chef_zero/endpoints/rest_list_endpoint.rb +40 -0
  34. data/lib/chef_zero/endpoints/rest_object_endpoint.rb +61 -0
  35. data/lib/chef_zero/endpoints/role_endpoint.rb +16 -0
  36. data/lib/chef_zero/endpoints/role_environments_endpoint.rb +14 -0
  37. data/lib/chef_zero/endpoints/sandbox_endpoint.rb +27 -0
  38. data/lib/chef_zero/endpoints/sandboxes_endpoint.rb +51 -0
  39. data/lib/chef_zero/endpoints/search_endpoint.rb +188 -0
  40. data/lib/chef_zero/endpoints/searches_endpoint.rb +18 -0
  41. data/lib/chef_zero/log.rb +7 -0
  42. data/lib/chef_zero/rest_base.rb +133 -0
  43. data/lib/chef_zero/rest_error_response.rb +11 -0
  44. data/lib/chef_zero/rest_request.rb +56 -0
  45. data/lib/chef_zero/rest_router.rb +44 -0
  46. data/lib/chef_zero/rspec.rb +107 -0
  47. data/lib/chef_zero/server.rb +309 -0
  48. data/lib/chef_zero/solr/query/binary_operator.rb +53 -0
  49. data/lib/chef_zero/solr/query/phrase.rb +23 -0
  50. data/lib/chef_zero/solr/query/range_query.rb +34 -0
  51. data/lib/chef_zero/solr/query/regexpable_query.rb +29 -0
  52. data/lib/chef_zero/solr/query/subquery.rb +35 -0
  53. data/lib/chef_zero/solr/query/term.rb +45 -0
  54. data/lib/chef_zero/solr/query/unary_operator.rb +43 -0
  55. data/lib/chef_zero/solr/solr_doc.rb +62 -0
  56. data/lib/chef_zero/solr/solr_parser.rb +193 -0
  57. data/lib/chef_zero/version.rb +3 -0
  58. data/spec/run.rb +25 -0
  59. data/spec/support/pedant.rb +117 -0
  60. data/spec/support/stickywicket.pem +27 -0
  61. metadata +204 -0
@@ -0,0 +1,142 @@
1
+ require 'chef_zero'
2
+ require 'chef_zero/rest_base'
3
+
4
+ module ChefZero
5
+ class DataNormalizer
6
+ def self.normalize_client(client, name)
7
+ client['name'] ||= name
8
+ client['admin'] ||= false
9
+ client['public_key'] ||= PUBLIC_KEY
10
+ client['validator'] ||= false
11
+ client['json_class'] ||= "Chef::ApiClient"
12
+ client['chef_type'] ||= "client"
13
+ client
14
+ end
15
+
16
+ def self.normalize_user(user, name)
17
+ user['name'] ||= name
18
+ user['admin'] ||= false
19
+ user['public_key'] ||= PUBLIC_KEY
20
+ user
21
+ end
22
+
23
+ def self.normalize_data_bag_item(data_bag_item, data_bag_name, id, method)
24
+ if method == 'DELETE'
25
+ # TODO SERIOUSLY, WHO DOES THIS MANY EXCEPTIONS IN THEIR INTERFACE
26
+ if !(data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data'])
27
+ data_bag_item['id'] ||= id
28
+ data_bag_item = { 'raw_data' => data_bag_item }
29
+ data_bag_item['chef_type'] ||= 'data_bag_item'
30
+ data_bag_item['json_class'] ||= 'Chef::DataBagItem'
31
+ data_bag_item['data_bag'] ||= data_bag_name
32
+ data_bag_item['name'] ||= "data_bag_item_#{data_bag_name}_#{id}"
33
+ end
34
+ else
35
+ # If it's not already wrapped with raw_data, wrap it.
36
+ if data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data']
37
+ data_bag_item = data_bag_item['raw_data']
38
+ end
39
+ # Argh. We don't do this on GET, but we do on PUT and POST????
40
+ if %w(PUT POST).include?(method)
41
+ data_bag_item['chef_type'] ||= 'data_bag_item'
42
+ data_bag_item['data_bag'] ||= data_bag_name
43
+ end
44
+ data_bag_item['id'] ||= id
45
+ end
46
+ data_bag_item
47
+ end
48
+
49
+ def self.normalize_environment(environment, name)
50
+ environment['name'] ||= name
51
+ environment['description'] ||= ''
52
+ environment['cookbook_versions'] ||= {}
53
+ environment['json_class'] ||= "Chef::Environment"
54
+ environment['chef_type'] ||= "environment"
55
+ environment['default_attributes'] ||= {}
56
+ environment['override_attributes'] ||= {}
57
+ environment
58
+ end
59
+
60
+ def self.normalize_cookbook(cookbook, name, version, base_uri, method)
61
+ # TODO I feel dirty
62
+ if method != 'PUT'
63
+ cookbook.each_pair do |key, value|
64
+ if value.is_a?(Array)
65
+ value.each do |file|
66
+ if file.is_a?(Hash) && file.has_key?('checksum')
67
+ file['url'] ||= RestBase::build_uri(base_uri, ['file_store', 'checksums', file['checksum']])
68
+ end
69
+ end
70
+ end
71
+ end
72
+ cookbook['name'] ||= "#{name}-#{version}"
73
+ # TODO this feels wrong, but the real chef server doesn't expand this default
74
+ # cookbook['version'] ||= version
75
+ cookbook['cookbook_name'] ||= name
76
+ cookbook['frozen?'] ||= false
77
+ cookbook['metadata'] ||= {}
78
+ cookbook['metadata']['version'] ||= version
79
+ # Sad to not be expanding defaults just because Chef doesn't :(
80
+ # cookbook['metadata']['name'] ||= name
81
+ # cookbook['metadata']['description'] ||= "A fabulous new cookbook"
82
+ cookbook['metadata']['long_description'] ||= ""
83
+ # cookbook['metadata']['maintainer'] ||= "YOUR_COMPANY_NAME"
84
+ # cookbook['metadata']['maintainer_email'] ||= "YOUR_EMAIL"
85
+ # cookbook['metadata']['license'] ||= "none"
86
+ cookbook['metadata']['dependencies'] ||= {}
87
+ cookbook['metadata']['attributes'] ||= {}
88
+ cookbook['metadata']['recipes'] ||= {}
89
+ end
90
+ cookbook['json_class'] ||= 'Chef::CookbookVersion'
91
+ cookbook['chef_type'] ||= 'cookbook_version'
92
+ if method == 'MIN'
93
+ cookbook['metadata'].delete('attributes')
94
+ cookbook['metadata'].delete('long_description')
95
+ end
96
+ cookbook
97
+ end
98
+
99
+ def self.normalize_node(node, name)
100
+ node['name'] ||= name
101
+ node['json_class'] ||= 'Chef::Node'
102
+ node['chef_type'] ||= 'node'
103
+ node['chef_environment'] ||= '_default'
104
+ node['override'] ||= {}
105
+ node['normal'] ||= {}
106
+ node['default'] ||= {}
107
+ node['automatic'] ||= {}
108
+ node['run_list'] ||= []
109
+ node['run_list'] = normalize_run_list(node['run_list'])
110
+ node
111
+ end
112
+
113
+ def self.normalize_role(role, name)
114
+ role['name'] ||= name
115
+ role['description'] ||= ''
116
+ role['json_class'] ||= 'Chef::Role'
117
+ role['chef_type'] ||= 'role'
118
+ role['default_attributes'] ||= {}
119
+ role['override_attributes'] ||= {}
120
+ role['run_list'] ||= []
121
+ role['run_list'] = normalize_run_list(role['run_list'])
122
+ role['env_run_lists'] ||= {}
123
+ role['env_run_lists'].each_pair do |env, run_list|
124
+ role['env_run_lists'][env] = normalize_run_list(run_list)
125
+ end
126
+ role
127
+ end
128
+
129
+ def self.normalize_run_list(run_list)
130
+ run_list.map{|item|
131
+ case item
132
+ when /^recipe\[.*\]$/
133
+ item # explicit recipe
134
+ when /^role\[.*\]$/
135
+ item # explicit role
136
+ else
137
+ "recipe[#{item}]"
138
+ end
139
+ }.uniq
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,29 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2013 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef_zero/data_store/data_error'
20
+
21
+ module ChefZero
22
+ module DataStore
23
+ class DataAlreadyExistsError < DataError
24
+ def initialize(path, cause = nil)
25
+ super
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2013 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ module ChefZero
20
+ module DataStore
21
+ class DataError < StandardError
22
+ def initialize(path, cause = nil)
23
+ @path = path
24
+ @cause = cause
25
+ end
26
+
27
+ attr_reader :path
28
+ attr_reader :cause
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2013 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef_zero/data_store/data_error'
20
+
21
+ module ChefZero
22
+ module DataStore
23
+ class DataNotFoundError < DataError
24
+ def initialize(path, cause = nil)
25
+ super
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,167 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2013 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef_zero/data_store/data_already_exists_error'
20
+ require 'chef_zero/data_store/data_not_found_error'
21
+
22
+ module ChefZero
23
+ module DataStore
24
+ class MemoryStore
25
+ def initialize
26
+ clear
27
+ end
28
+
29
+ def clear
30
+ @data = {}
31
+
32
+ # Create containers
33
+ create_dir([], 'clients')
34
+ create_dir([], 'cookbooks')
35
+ create_dir([], 'data')
36
+ create_dir([], 'environments')
37
+ create_dir([], 'file_store')
38
+ create_dir(['file_store'], 'checksums')
39
+ create_dir([], 'nodes')
40
+ create_dir([], 'roles')
41
+ create_dir([], 'sandboxes')
42
+ create_dir([], 'users')
43
+
44
+ # Set defaults
45
+ create(['clients'], 'chef-validator', '{ "validator": true }')
46
+ create(['clients'], 'chef-webui', '{ "admin": true }')
47
+ create(['environments'], '_default', '{ "description": "The default Chef environment" }')
48
+ create(['users'], 'admin', '{ "admin": true }')
49
+ end
50
+
51
+ def create_dir(path, name, *options)
52
+ parent = _get(path, options.include?(:recursive))
53
+
54
+ if parent.has_key?(name)
55
+ if !options.include?(:recursive)
56
+ raise DataAlreadyExistsError.new(path + [name])
57
+ end
58
+ else
59
+ parent[name] = {}
60
+ end
61
+ end
62
+
63
+ def create(path, name, data, *options)
64
+ if !data.is_a?(String)
65
+ raise "set only works with strings"
66
+ end
67
+
68
+ parent = _get(path, options.include?(:create_dir))
69
+
70
+ if parent.has_key?(name)
71
+ raise DataAlreadyExistsError.new(path + [name])
72
+ end
73
+ parent[name] = data
74
+ end
75
+
76
+ def get(path, request=nil)
77
+ value = _get(path)
78
+ if value.is_a?(Hash)
79
+ raise "get() called on directory #{path.join('/')}"
80
+ end
81
+ value
82
+ end
83
+
84
+ def set(path, data, *options)
85
+ if !data.is_a?(String)
86
+ raise "set only works with strings: #{path} = #{data.inspect}"
87
+ end
88
+
89
+ # Get the parent
90
+ parent = _get(path[0..-2], options.include?(:create_dir))
91
+
92
+ if !options.include?(:create) && !parent[path[-1]]
93
+ raise DataNotFoundError.new(path)
94
+ end
95
+ parent[path[-1]] = data
96
+ end
97
+
98
+ def delete(path)
99
+ parent = _get(path[0,path.length-1])
100
+ if !parent.has_key?(path[-1])
101
+ raise DataNotFoundError.new(path)
102
+ end
103
+ if !parent[path[-1]].is_a?(String)
104
+ raise "delete only works with strings: #{path}"
105
+ end
106
+ parent.delete(path[-1])
107
+ end
108
+
109
+ def delete_dir(path)
110
+ parent = _get(path[0,path.length-1])
111
+ if !parent.has_key?(path[-1])
112
+ raise DataNotFoundError.new(path)
113
+ end
114
+ if !parent[path[-1]].is_a?(Hash)
115
+ raise "delete_dir only works with directories: #{path}"
116
+ end
117
+ parent.delete(path[-1])
118
+ end
119
+
120
+ def list(path)
121
+ dir = _get(path)
122
+ if !dir.is_a? Hash
123
+ raise "list only works with directories (#{path} = #{dir.class}"
124
+ end
125
+ dir.keys.sort
126
+ end
127
+
128
+ def exists?(path)
129
+ begin
130
+ get(path)
131
+ return true
132
+ rescue DataNotFoundError
133
+ return false
134
+ end
135
+ end
136
+
137
+ def exists_dir?(path)
138
+ begin
139
+ dir = _get(path)
140
+ if !dir.is_a? Hash
141
+ raise "exists_dir? only works with directories (#{path} = #{dir.class}"
142
+ end
143
+ return true
144
+ rescue DataNotFoundError
145
+ return false
146
+ end
147
+ end
148
+
149
+ private
150
+
151
+ def _get(path, create_dir=false)
152
+ value = @data
153
+ path.each_with_index do |path_part, index|
154
+ if !value.has_key?(path_part)
155
+ if create_dir
156
+ value[path_part] = {}
157
+ else
158
+ raise DataNotFoundError.new(path[0,index+1])
159
+ end
160
+ end
161
+ value = value[path_part]
162
+ end
163
+ value
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,68 @@
1
+ require 'json'
2
+ require 'chef_zero/endpoints/rest_object_endpoint'
3
+ require 'chef_zero/data_normalizer'
4
+
5
+ module ChefZero
6
+ module Endpoints
7
+ # /clients/* and /users/*
8
+ class ActorEndpoint < RestObjectEndpoint
9
+ def put(request)
10
+ # Find out if we're updating the public key.
11
+ request_body = JSON.parse(request.body, :create_additions => false)
12
+ if request_body['public_key'].nil?
13
+ # If public_key is null, then don't overwrite it. Weird patchiness.
14
+ body_modified = true
15
+ request_body.delete('public_key')
16
+ else
17
+ updating_public_key = true
18
+ end
19
+
20
+ # Generate private_key if requested.
21
+ if request_body.has_key?('private_key')
22
+ body_modified = true
23
+ if request_body['private_key']
24
+ private_key, public_key = server.gen_key_pair
25
+ updating_public_key = true
26
+ request_body['public_key'] = public_key
27
+ end
28
+ request_body.delete('private_key')
29
+ end
30
+
31
+ # Save request
32
+ request.body = JSON.pretty_generate(request_body) if body_modified
33
+
34
+ # PUT /clients is patchy
35
+ request.body = patch_request_body(request)
36
+
37
+ result = super(request)
38
+
39
+ # Inject private_key into response, delete public_key/password if applicable
40
+ if result[0] == 200
41
+ response = JSON.parse(result[2], :create_additions => false)
42
+ response['private_key'] = private_key if private_key
43
+ response.delete('public_key') if !updating_public_key && request.rest_path[0] == 'users'
44
+ response.delete('password')
45
+ # For PUT /clients, a rename returns 201.
46
+ if request_body['name'] && request.rest_path[1] != request_body['name']
47
+ json_response(201, response)
48
+ else
49
+ json_response(200, response)
50
+ end
51
+ else
52
+ result
53
+ end
54
+ end
55
+
56
+ def populate_defaults(request, response_json)
57
+ response = JSON.parse(response_json, :create_additions => false)
58
+ if request.rest_path[0] == 'clients'
59
+ response = DataNormalizer.normalize_client(response, request.rest_path[1])
60
+ else
61
+ response = DataNormalizer.normalize_user(response, request.rest_path[1])
62
+ end
63
+ JSON.pretty_generate(response)
64
+ end
65
+ end
66
+ end
67
+ end
68
+