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