clc-cheffish 0.8.clc

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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/README.md +4 -0
  4. data/Rakefile +23 -0
  5. data/lib/chef/provider/chef_acl.rb +434 -0
  6. data/lib/chef/provider/chef_client.rb +48 -0
  7. data/lib/chef/provider/chef_container.rb +50 -0
  8. data/lib/chef/provider/chef_data_bag.rb +50 -0
  9. data/lib/chef/provider/chef_data_bag_item.rb +273 -0
  10. data/lib/chef/provider/chef_environment.rb +78 -0
  11. data/lib/chef/provider/chef_group.rb +78 -0
  12. data/lib/chef/provider/chef_mirror.rb +138 -0
  13. data/lib/chef/provider/chef_node.rb +82 -0
  14. data/lib/chef/provider/chef_organization.rb +150 -0
  15. data/lib/chef/provider/chef_resolved_cookbooks.rb +41 -0
  16. data/lib/chef/provider/chef_role.rb +79 -0
  17. data/lib/chef/provider/chef_user.rb +53 -0
  18. data/lib/chef/provider/private_key.rb +219 -0
  19. data/lib/chef/provider/public_key.rb +82 -0
  20. data/lib/chef/resource/chef_acl.rb +65 -0
  21. data/lib/chef/resource/chef_client.rb +44 -0
  22. data/lib/chef/resource/chef_container.rb +18 -0
  23. data/lib/chef/resource/chef_data_bag.rb +18 -0
  24. data/lib/chef/resource/chef_data_bag_item.rb +114 -0
  25. data/lib/chef/resource/chef_environment.rb +71 -0
  26. data/lib/chef/resource/chef_group.rb +49 -0
  27. data/lib/chef/resource/chef_mirror.rb +47 -0
  28. data/lib/chef/resource/chef_node.rb +18 -0
  29. data/lib/chef/resource/chef_organization.rb +64 -0
  30. data/lib/chef/resource/chef_resolved_cookbooks.rb +31 -0
  31. data/lib/chef/resource/chef_role.rb +104 -0
  32. data/lib/chef/resource/chef_user.rb +51 -0
  33. data/lib/chef/resource/private_key.rb +44 -0
  34. data/lib/chef/resource/public_key.rb +21 -0
  35. data/lib/cheffish.rb +222 -0
  36. data/lib/cheffish/actor_provider_base.rb +131 -0
  37. data/lib/cheffish/basic_chef_client.rb +115 -0
  38. data/lib/cheffish/chef_provider_base.rb +231 -0
  39. data/lib/cheffish/chef_run_data.rb +19 -0
  40. data/lib/cheffish/chef_run_listener.rb +28 -0
  41. data/lib/cheffish/key_formatter.rb +109 -0
  42. data/lib/cheffish/merged_config.rb +94 -0
  43. data/lib/cheffish/recipe_dsl.rb +147 -0
  44. data/lib/cheffish/server_api.rb +52 -0
  45. data/lib/cheffish/version.rb +3 -0
  46. data/lib/cheffish/with_pattern.rb +21 -0
  47. data/spec/functional/fingerprint_spec.rb +64 -0
  48. data/spec/functional/merged_config_spec.rb +20 -0
  49. data/spec/integration/chef_acl_spec.rb +914 -0
  50. data/spec/integration/chef_client_spec.rb +110 -0
  51. data/spec/integration/chef_container_spec.rb +34 -0
  52. data/spec/integration/chef_group_spec.rb +324 -0
  53. data/spec/integration/chef_mirror_spec.rb +244 -0
  54. data/spec/integration/chef_node_spec.rb +211 -0
  55. data/spec/integration/chef_organization_spec.rb +244 -0
  56. data/spec/integration/chef_user_spec.rb +90 -0
  57. data/spec/integration/private_key_spec.rb +446 -0
  58. data/spec/integration/recipe_dsl_spec.rb +29 -0
  59. data/spec/support/key_support.rb +29 -0
  60. data/spec/support/repository_support.rb +103 -0
  61. data/spec/support/spec_support.rb +176 -0
  62. data/spec/unit/get_private_key_spec.rb +93 -0
  63. metadata +162 -0
@@ -0,0 +1,41 @@
1
+ require 'chef/provider/lwrp_base'
2
+ require 'chef_zero'
3
+
4
+ class Chef::Provider::ChefResolvedCookbooks < Chef::Provider::LWRPBase
5
+
6
+ action :resolve do
7
+ new_resource.cookbooks_from.each do |path|
8
+ ::Dir.entries(path).each do |name|
9
+ if ::File.directory?(::File.join(path, name)) && name != '.' && name != '..'
10
+ new_resource.berksfile.cookbook name, :path => ::File.join(path, name)
11
+ end
12
+ end
13
+ end
14
+
15
+ new_resource.berksfile.install
16
+
17
+ # Ridley really really wants a key :/
18
+ if new_resource.chef_server[:options][:signing_key_filename]
19
+ new_resource.berksfile.upload(
20
+ :server_url => new_resource.chef_server[:chef_server_url],
21
+ :client_name => new_resource.chef_server[:options][:client_name],
22
+ :client_key => new_resource.chef_server[:options][:signing_key_filename])
23
+ else
24
+ file = Tempfile.new('privatekey')
25
+ begin
26
+ file.write(ChefZero::PRIVATE_KEY)
27
+ file.close
28
+
29
+ new_resource.berksfile.upload(
30
+ :server_url => new_resource.chef_server[:chef_server_url],
31
+ :client_name => new_resource.chef_server[:options][:client_name] || 'me',
32
+ :client_key => file.path)
33
+
34
+ ensure
35
+ file.close
36
+ file.unlink
37
+ end
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,79 @@
1
+ require 'cheffish/chef_provider_base'
2
+ require 'chef/resource/chef_role'
3
+ require 'chef/chef_fs/data_handler/role_data_handler'
4
+
5
+ class Chef::Provider::ChefRole < Cheffish::ChefProviderBase
6
+
7
+ def whyrun_supported?
8
+ true
9
+ end
10
+
11
+ action :create do
12
+ differences = json_differences(current_json, new_json)
13
+
14
+ if current_resource_exists?
15
+ if differences.size > 0
16
+ description = [ "update role #{new_resource.name} at #{rest.url}" ] + differences
17
+ converge_by description do
18
+ rest.put("roles/#{new_resource.name}", normalize_for_put(new_json))
19
+ end
20
+ end
21
+ else
22
+ description = [ "create role #{new_resource.name} at #{rest.url}" ] + differences
23
+ converge_by description do
24
+ rest.post("roles", normalize_for_post(new_json))
25
+ end
26
+ end
27
+ end
28
+
29
+ action :delete do
30
+ if current_resource_exists?
31
+ converge_by "delete role #{new_resource.name} at #{rest.url}" do
32
+ rest.delete("roles/#{new_resource.name}")
33
+ end
34
+ end
35
+ end
36
+
37
+ def load_current_resource
38
+ begin
39
+ @current_resource = json_to_resource(rest.get("roles/#{new_resource.name}"))
40
+ rescue Net::HTTPServerException => e
41
+ if e.response.code == "404"
42
+ @current_resource = not_found_resource
43
+ else
44
+ raise
45
+ end
46
+ end
47
+ end
48
+
49
+ def augment_new_json(json)
50
+ # Apply modifiers
51
+ json['run_list'] = apply_run_list_modifiers(new_resource.run_list_modifiers, new_resource.run_list_removers, json['run_list'])
52
+ json['default_attributes'] = apply_modifiers(new_resource.default_attribute_modifiers, json['default_attributes'])
53
+ json['override_attributes'] = apply_modifiers(new_resource.override_attribute_modifiers, json['override_attributes'])
54
+ json
55
+ end
56
+
57
+ #
58
+ # Helpers
59
+ #
60
+
61
+ def resource_class
62
+ Chef::Resource::ChefRole
63
+ end
64
+
65
+ def data_handler
66
+ Chef::ChefFS::DataHandler::RoleDataHandler.new
67
+ end
68
+
69
+ def keys
70
+ {
71
+ 'name' => :name,
72
+ 'description' => :description,
73
+ 'run_list' => :run_list,
74
+ 'env_run_lists' => :env_run_lists,
75
+ 'default_attributes' => :default_attributes,
76
+ 'override_attributes' => :override_attributes
77
+ }
78
+ end
79
+ end
@@ -0,0 +1,53 @@
1
+ require 'cheffish/actor_provider_base'
2
+ require 'chef/resource/chef_user'
3
+ require 'chef/chef_fs/data_handler/user_data_handler'
4
+
5
+ class Chef::Provider::ChefUser < Cheffish::ActorProviderBase
6
+
7
+ def whyrun_supported?
8
+ true
9
+ end
10
+
11
+ action :create do
12
+ create_actor
13
+ end
14
+
15
+ action :delete do
16
+ delete_actor
17
+ end
18
+
19
+ #
20
+ # Helpers
21
+ #
22
+ # Gives us new_json, current_json, not_found_json, etc.
23
+
24
+ def actor_type
25
+ 'user'
26
+ end
27
+
28
+ def actor_path
29
+ "#{rest.root_url}/users"
30
+ end
31
+
32
+ def resource_class
33
+ Chef::Resource::ChefUser
34
+ end
35
+
36
+ def data_handler
37
+ Chef::ChefFS::DataHandler::UserDataHandler.new
38
+ end
39
+
40
+ def keys
41
+ {
42
+ 'name' => :name,
43
+ 'username' => :name,
44
+ 'admin' => :admin,
45
+ 'email' => :email,
46
+ 'password' => :password,
47
+ 'external_authentication_uid' => :external_authentication_uid,
48
+ 'recovery_authentication_enabled' => :recovery_authentication_enabled,
49
+ 'public_key' => :source_key
50
+ }
51
+ end
52
+
53
+ end
@@ -0,0 +1,219 @@
1
+ require 'chef/provider/lwrp_base'
2
+ require 'openssl'
3
+ require 'cheffish/key_formatter'
4
+
5
+ class Chef::Provider::PrivateKey < Chef::Provider::LWRPBase
6
+
7
+ action :create do
8
+ create_key(false, :create)
9
+ end
10
+
11
+ action :regenerate do
12
+ create_key(true, :regenerate)
13
+ end
14
+
15
+ action :delete do
16
+ if current_resource.path
17
+ converge_by "delete private key #{new_path}" do
18
+ ::File.unlink(new_path)
19
+ end
20
+ end
21
+ end
22
+
23
+ use_inline_resources
24
+
25
+ def whyrun_supported?
26
+ true
27
+ end
28
+
29
+ def create_key(regenerate, action)
30
+ if @should_create_directory
31
+ Cheffish.inline_resource(self, action) do
32
+ directory run_context.config[:private_key_write_path]
33
+ end
34
+ end
35
+
36
+ final_private_key = nil
37
+ if new_source_key
38
+ #
39
+ # Create private key from source
40
+ #
41
+ desired_output = encode_private_key(new_source_key)
42
+ if current_resource.path == :none || desired_output != IO.read(new_path)
43
+ converge_by "reformat key at #{new_resource.source_key_path} to #{new_resource.format} private key #{new_path} (#{new_resource.pass_phrase ? ", #{new_resource.cipher} password" : ""})" do
44
+ IO.write(new_path, desired_output)
45
+ end
46
+ end
47
+
48
+ final_private_key = new_source_key
49
+
50
+ else
51
+ #
52
+ # Generate a new key
53
+ #
54
+ if current_resource.action == [ :delete ] || regenerate ||
55
+ (new_resource.regenerate_if_different &&
56
+ (!current_private_key ||
57
+ current_resource.size != new_resource.size ||
58
+ current_resource.type != new_resource.type))
59
+
60
+ case new_resource.type
61
+ when :rsa
62
+ if new_resource.exponent
63
+ final_private_key = OpenSSL::PKey::RSA.generate(new_resource.size, new_resource.exponent)
64
+ else
65
+ final_private_key = OpenSSL::PKey::RSA.generate(new_resource.size)
66
+ end
67
+ when :dsa
68
+ final_private_key = OpenSSL::PKey::DSA.generate(new_resource.size)
69
+ end
70
+
71
+ generated_key = true
72
+ elsif !current_private_key
73
+ raise "Could not read private key from #{current_resource.path}: missing pass phrase?"
74
+ else
75
+ final_private_key = current_private_key
76
+ generated_key = false
77
+ end
78
+
79
+ if generated_key
80
+ generated_description = " (#{new_resource.size} bits#{new_resource.pass_phrase ? ", #{new_resource.cipher} password" : ""})"
81
+
82
+ if new_path != :none
83
+ action = current_resource.path == :none ? 'create' : 'overwrite'
84
+ converge_by "#{action} #{new_resource.type} private key #{new_path}#{generated_description}" do
85
+ write_private_key(final_private_key)
86
+ end
87
+ else
88
+ converge_by "generate private key#{generated_description}" do
89
+ end
90
+ end
91
+ else
92
+ # Warn if existing key has different characteristics than expected
93
+ if current_resource.size != new_resource.size
94
+ Chef::Log.warn("Mismatched key size! #{current_resource.path} is #{current_resource.size} bytes, desired is #{new_resource.size} bytes. Use action :regenerate to force key regeneration.")
95
+ elsif current_resource.type != new_resource.type
96
+ Chef::Log.warn("Mismatched key type! #{current_resource.path} is #{current_resource.type}, desired is #{new_resource.type} bytes. Use action :regenerate to force key regeneration.")
97
+ end
98
+
99
+ if current_resource.format != new_resource.format
100
+ converge_by "change format of #{new_resource.type} private key #{new_path} from #{current_resource.format} to #{new_resource.format}" do
101
+ write_private_key(current_private_key)
102
+ end
103
+ elsif (@current_file_mode & 0077) != 0
104
+ new_mode = @current_file_mode & 07700
105
+ converge_by "change mode of private key #{new_path} to #{new_mode.to_s(8)}" do
106
+ ::File.chmod(new_mode, new_path)
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ if new_resource.public_key_path
113
+ public_key_path = new_resource.public_key_path
114
+ public_key_format = new_resource.public_key_format
115
+ Cheffish.inline_resource(self, action) do
116
+ public_key public_key_path do
117
+ source_key final_private_key
118
+ format public_key_format
119
+ end
120
+ end
121
+ end
122
+
123
+ if new_resource.after
124
+ new_resource.after.call(new_resource, final_private_key)
125
+ end
126
+ end
127
+
128
+ def encode_private_key(key)
129
+ key_format = {}
130
+ key_format[:format] = new_resource.format if new_resource.format
131
+ key_format[:pass_phrase] = new_resource.pass_phrase if new_resource.pass_phrase
132
+ key_format[:cipher] = new_resource.cipher if new_resource.cipher
133
+ Cheffish::KeyFormatter.encode(key, key_format)
134
+ end
135
+
136
+ def write_private_key(key)
137
+ ::File.open(new_path, 'w') do |file|
138
+ file.chmod(0600)
139
+ file.write(encode_private_key(key))
140
+ end
141
+ end
142
+
143
+ def new_source_key
144
+ @new_source_key ||= begin
145
+ if new_resource.source_key.is_a?(String)
146
+ source_key, source_key_format = Cheffish::KeyFormatter.decode(new_resource.source_key, new_resource.source_key_pass_phrase)
147
+ source_key
148
+ elsif new_resource.source_key
149
+ new_resource.source_key
150
+ elsif new_resource.source_key_path
151
+ source_key, source_key_format = Cheffish::KeyFormatter.decode(IO.read(new_resource.source_key_path), new_resource.source_key_pass_phrase, new_resource.source_key_path)
152
+ source_key
153
+ else
154
+ nil
155
+ end
156
+ end
157
+ end
158
+
159
+ attr_reader :current_private_key
160
+
161
+ def new_path
162
+ new_key_with_path[1]
163
+ end
164
+
165
+ def new_key_with_path
166
+ path = new_resource.path
167
+ if path.is_a?(Symbol)
168
+ return [ nil, path ]
169
+ elsif Pathname.new(path).relative?
170
+ private_key, private_key_path = Cheffish.get_private_key_with_path(path, run_context.config)
171
+ if private_key
172
+ return [ private_key, (private_key_path || :none) ]
173
+ elsif run_context.config[:private_key_write_path]
174
+ @should_create_directory = true
175
+ path = ::File.join(run_context.config[:private_key_write_path], path)
176
+ return [ nil, path ]
177
+ else
178
+ raise "Could not find key #{path} and Chef::Config.private_key_write_path is not set."
179
+ end
180
+ elsif ::File.exist?(path)
181
+ return [ IO.read(path), path ]
182
+ else
183
+ return [ nil, path ]
184
+ end
185
+ end
186
+
187
+ def load_current_resource
188
+ resource = Chef::Resource::PrivateKey.new(new_resource.name, run_context)
189
+
190
+ new_key, new_path = new_key_with_path
191
+ if new_path != :none && ::File.exist?(new_path)
192
+ resource.path new_path
193
+ @current_file_mode = ::File.stat(new_path).mode
194
+ else
195
+ resource.path :none
196
+ end
197
+
198
+ if new_key
199
+ begin
200
+ key, key_format = Cheffish::KeyFormatter.decode(new_key, new_resource.pass_phrase, new_path)
201
+ if key
202
+ @current_private_key = key
203
+ resource.format key_format[:format]
204
+ resource.type key_format[:type]
205
+ resource.size key_format[:size]
206
+ resource.exponent key_format[:exponent]
207
+ resource.pass_phrase key_format[:pass_phrase]
208
+ resource.cipher key_format[:cipher]
209
+ end
210
+ rescue
211
+ # If there's an error reading, we assume format and type are wrong and don't futz with them
212
+ end
213
+ else
214
+ resource.action :delete
215
+ end
216
+
217
+ @current_resource = resource
218
+ end
219
+ end
@@ -0,0 +1,82 @@
1
+ require 'chef/provider/lwrp_base'
2
+ require 'openssl'
3
+ require 'cheffish/key_formatter'
4
+
5
+ class Chef::Provider::PublicKey < Chef::Provider::LWRPBase
6
+ action :create do
7
+ if !new_source_key
8
+ raise "No source key specified"
9
+ end
10
+ desired_output = encode_public_key(new_source_key)
11
+ if Array(current_resource.action) == [ :delete ] || desired_output != IO.read(new_resource.path)
12
+ converge_by "write #{new_resource.format} public key #{new_resource.path} from #{new_source_key_publicity} key #{new_resource.source_key_path}" do
13
+ IO.write(new_resource.path, desired_output)
14
+ # TODO permissions on file?
15
+ end
16
+ end
17
+ end
18
+
19
+ action :delete do
20
+ if Array(current_resource.action) == [ :create ]
21
+ converge_by "delete public key #{new_resource.path}" do
22
+ ::File.unlink(new_resource.path)
23
+ end
24
+ end
25
+ end
26
+
27
+ def whyrun_supported?
28
+ true
29
+ end
30
+
31
+ def encode_public_key(key)
32
+ key_format = {}
33
+ key_format[:format] = new_resource.format if new_resource.format
34
+ Cheffish::KeyFormatter.encode(key, key_format)
35
+ end
36
+
37
+ attr_reader :current_public_key
38
+ attr_reader :new_source_key_publicity
39
+
40
+ def new_source_key
41
+ @new_source_key ||= begin
42
+ if new_resource.source_key.is_a?(String)
43
+ source_key, source_key_format = Cheffish::KeyFormatter.decode(new_resource.source_key, new_resource.source_key_pass_phrase)
44
+ elsif new_resource.source_key
45
+ source_key = new_resource.source_key
46
+ elsif new_resource.source_key_path
47
+ source_key, source_key_format = Cheffish::KeyFormatter.decode(IO.read(new_resource.source_key_path), new_resource.source_key_pass_phrase, new_resource.source_key_path)
48
+ else
49
+ return nil
50
+ end
51
+
52
+ if source_key.private?
53
+ @new_source_key_publicity = 'private'
54
+ source_key.public_key
55
+ else
56
+ @new_source_key_publicity = 'public'
57
+ source_key
58
+ end
59
+ end
60
+ end
61
+
62
+ def load_current_resource
63
+ if ::File.exist?(new_resource.path)
64
+ resource = Chef::Resource::PublicKey.new(new_resource.path, run_context)
65
+ begin
66
+ key, key_format = Cheffish::KeyFormatter.decode(IO.read(new_resource.path), nil, new_resource.path)
67
+ if key
68
+ @current_public_key = key
69
+ resource.format key_format[:format]
70
+ end
71
+ rescue
72
+ # If there is an error reading we assume format and such is broken
73
+ end
74
+
75
+ @current_resource = resource
76
+ else
77
+ not_found_resource = Chef::Resource::PublicKey.new(new_resource.path, run_context)
78
+ not_found_resource.action :delete
79
+ @current_resource = not_found_resource
80
+ end
81
+ end
82
+ end