cheffish 0.1
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 +7 -0
- data/LICENSE +201 -0
- data/README.md +4 -0
- data/Rakefile +23 -0
- data/lib/chef/provider/chef_client.rb +44 -0
- data/lib/chef/provider/chef_data_bag.rb +50 -0
- data/lib/chef/provider/chef_data_bag_item.rb +273 -0
- data/lib/chef/provider/chef_environment.rb +78 -0
- data/lib/chef/provider/chef_node.rb +82 -0
- data/lib/chef/provider/chef_role.rb +79 -0
- data/lib/chef/provider/chef_user.rb +48 -0
- data/lib/chef/provider/private_key.rb +160 -0
- data/lib/chef/provider/public_key.rb +83 -0
- data/lib/chef/resource/chef_client.rb +44 -0
- data/lib/chef/resource/chef_data_bag.rb +18 -0
- data/lib/chef/resource/chef_data_bag_item.rb +114 -0
- data/lib/chef/resource/chef_environment.rb +71 -0
- data/lib/chef/resource/chef_node.rb +18 -0
- data/lib/chef/resource/chef_role.rb +104 -0
- data/lib/chef/resource/chef_user.rb +51 -0
- data/lib/chef/resource/in_parallel.rb +6 -0
- data/lib/chef/resource/private_key.rb +39 -0
- data/lib/chef/resource/public_key.rb +16 -0
- data/lib/cheffish.rb +245 -0
- data/lib/cheffish/actor_provider_base.rb +120 -0
- data/lib/cheffish/chef_provider_base.rb +222 -0
- data/lib/cheffish/cheffish_server_api.rb +21 -0
- data/lib/cheffish/inline_resource.rb +88 -0
- data/lib/cheffish/key_formatter.rb +93 -0
- data/lib/cheffish/recipe_dsl.rb +98 -0
- data/lib/cheffish/version.rb +4 -0
- data/spec/integration/chef_client_spec.rb +48 -0
- data/spec/integration/chef_node_spec.rb +75 -0
- data/spec/integration/chef_user_spec.rb +48 -0
- data/spec/integration/private_key_spec.rb +356 -0
- data/spec/integration/recipe_dsl_spec.rb +29 -0
- data/spec/support/key_support.rb +29 -0
- data/spec/support/spec_support.rb +148 -0
- metadata +124 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'cheffish/chef_provider_base'
|
2
|
+
require 'chef/resource/chef_environment'
|
3
|
+
require 'chef/chef_fs/data_handler/environment_data_handler'
|
4
|
+
|
5
|
+
class Chef::Provider::ChefEnvironment < 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 environment #{new_resource.name} at #{rest.url}" ] + differences
|
17
|
+
converge_by description do
|
18
|
+
rest.put("environments/#{new_resource.name}", normalize_for_put(new_json))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
else
|
22
|
+
description = [ "create environment #{new_resource.name} at #{rest.url}" ] + differences
|
23
|
+
converge_by description do
|
24
|
+
rest.post("environments", 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 environment #{new_resource.name} at #{rest.url}" do
|
32
|
+
rest.delete("environments/#{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("environments/#{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['default_attributes'] = apply_modifiers(new_resource.default_attribute_modifiers, json['default_attributes'])
|
52
|
+
json['override_attributes'] = apply_modifiers(new_resource.override_attribute_modifiers, json['override_attributes'])
|
53
|
+
json
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Helpers
|
58
|
+
#
|
59
|
+
|
60
|
+
def resource_class
|
61
|
+
Chef::Resource::ChefEnvironment
|
62
|
+
end
|
63
|
+
|
64
|
+
def data_handler
|
65
|
+
Chef::ChefFS::DataHandler::EnvironmentDataHandler.new
|
66
|
+
end
|
67
|
+
|
68
|
+
def keys
|
69
|
+
{
|
70
|
+
'name' => :name,
|
71
|
+
'description' => :description,
|
72
|
+
'cookbook_versions' => :cookbook_versions,
|
73
|
+
'default_attributes' => :default_attributes,
|
74
|
+
'override_attributes' => :override_attributes
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'cheffish/chef_provider_base'
|
2
|
+
require 'chef/resource/chef_node'
|
3
|
+
require 'chef/chef_fs/data_handler/node_data_handler'
|
4
|
+
|
5
|
+
class Chef::Provider::ChefNode < 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 node #{new_resource.name} at #{rest.url}" ] + differences
|
17
|
+
converge_by description do
|
18
|
+
rest.put("nodes/#{new_resource.name}", normalize_for_put(new_json))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
else
|
22
|
+
description = [ "create node #{new_resource.name} at #{rest.url}" ] + differences
|
23
|
+
converge_by description do
|
24
|
+
rest.post("nodes", 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 node #{new_resource.name} at #{rest.url}" do
|
32
|
+
rest.delete("nodes/#{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("nodes/#{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'] = apply_modifiers(new_resource.default_modifiers, json['default'])
|
53
|
+
json['normal'] = apply_modifiers(new_resource.normal_modifiers, json['normal'])
|
54
|
+
json['override'] = apply_modifiers(new_resource.override_modifiers, json['override'])
|
55
|
+
json['automatic'] = apply_modifiers(new_resource.automatic_modifiers, json['automatic'])
|
56
|
+
json
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Helpers
|
61
|
+
#
|
62
|
+
|
63
|
+
def resource_class
|
64
|
+
Chef::Resource::ChefNode
|
65
|
+
end
|
66
|
+
|
67
|
+
def data_handler
|
68
|
+
Chef::ChefFS::DataHandler::NodeDataHandler.new
|
69
|
+
end
|
70
|
+
|
71
|
+
def keys
|
72
|
+
{
|
73
|
+
'name' => :name,
|
74
|
+
'chef_environment' => :chef_environment,
|
75
|
+
'run_list' => :run_list,
|
76
|
+
'default' => :default_attributes,
|
77
|
+
'normal' => :normal_attributes,
|
78
|
+
'override' => :override_attributes,
|
79
|
+
'automatic' => :automatic_attributes
|
80
|
+
}
|
81
|
+
end
|
82
|
+
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,48 @@
|
|
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 resource_class
|
29
|
+
Chef::Resource::ChefUser
|
30
|
+
end
|
31
|
+
|
32
|
+
def data_handler
|
33
|
+
Chef::ChefFS::DataHandler::UserDataHandler.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def keys
|
37
|
+
{
|
38
|
+
'name' => :name,
|
39
|
+
'admin' => :admin,
|
40
|
+
'email' => :email,
|
41
|
+
'password' => :password,
|
42
|
+
'external_authentication_uid' => :external_authentication_uid,
|
43
|
+
'recovery_authentication_enabled' => :recovery_authentication_enabled,
|
44
|
+
'public_key' => :source_key
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,160 @@
|
|
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)
|
9
|
+
end
|
10
|
+
|
11
|
+
action :regenerate do
|
12
|
+
create_key(true)
|
13
|
+
end
|
14
|
+
|
15
|
+
action :delete do
|
16
|
+
if Array(current_resource.action) == [ :create ]
|
17
|
+
converge_by "delete private key #{new_resource.path}" do
|
18
|
+
::File.unlink(new_resource.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)
|
30
|
+
final_private_key = nil
|
31
|
+
if new_source_key
|
32
|
+
#
|
33
|
+
# Create private key from source
|
34
|
+
#
|
35
|
+
desired_output = encode_private_key(new_source_key)
|
36
|
+
if Array(current_resource.action) == [ :delete ] || desired_output != IO.read(new_resource.path)
|
37
|
+
converge_by "reformat key at #{new_resource.source_key_path} to #{new_resource.format} private key #{new_resource.path} (#{new_resource.pass_phrase ? ", #{new_resource.cipher} password" : ""})" do
|
38
|
+
IO.write(new_resource.path, desired_output)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
final_private_key = new_source_key
|
43
|
+
|
44
|
+
else
|
45
|
+
#
|
46
|
+
# Create private key file
|
47
|
+
#
|
48
|
+
if Array(current_resource.action) == [ :delete ] || regenerate ||
|
49
|
+
(new_resource.regenerate_if_different &&
|
50
|
+
(current_resource.size != new_resource.size ||
|
51
|
+
current_resource.type != new_resource.type))
|
52
|
+
action = (current_resource.path == :none || ::File.exists?(current_resource.path)) ? "overwrite" : "create"
|
53
|
+
converge_by "#{action} #{new_resource.type} private key #{new_resource.path} (#{new_resource.size} bits#{new_resource.pass_phrase ? ", #{new_resource.cipher} password" : ""})" do
|
54
|
+
case new_resource.type
|
55
|
+
when :rsa
|
56
|
+
if new_resource.exponent
|
57
|
+
final_private_key = OpenSSL::PKey::RSA.generate(new_resource.size, new_resource.exponent)
|
58
|
+
else
|
59
|
+
final_private_key = OpenSSL::PKey::RSA.generate(new_resource.size)
|
60
|
+
end
|
61
|
+
when :dsa
|
62
|
+
final_private_key = OpenSSL::PKey::DSA.generate(new_resource.size)
|
63
|
+
end
|
64
|
+
|
65
|
+
if new_resource.path != :none
|
66
|
+
write_private_key(final_private_key)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
# Warn if existing key has different characteristics than expected
|
71
|
+
if current_resource.size != new_resource.size
|
72
|
+
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.")
|
73
|
+
elsif current_resource.type != new_resource.type
|
74
|
+
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.")
|
75
|
+
end
|
76
|
+
|
77
|
+
final_private_key = current_private_key
|
78
|
+
|
79
|
+
if current_resource.format != new_resource.format
|
80
|
+
converge_by "change format of #{new_resource.type} private key #{new_resource.path} from #{current_resource.format} to #{new_resource.format}" do
|
81
|
+
write_private_key(current_private_key)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if new_resource.public_key_path
|
88
|
+
public_key_path = new_resource.public_key_path
|
89
|
+
public_key_format = new_resource.public_key_format
|
90
|
+
Cheffish.inline_resource(self) do
|
91
|
+
public_key public_key_path do
|
92
|
+
source_key final_private_key
|
93
|
+
format public_key_format
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
if new_resource.after
|
99
|
+
new_resource.after.call(new_resource, final_private_key)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def encode_private_key(key)
|
104
|
+
key_format = {}
|
105
|
+
key_format[:format] = new_resource.format if new_resource.format
|
106
|
+
key_format[:pass_phrase] = new_resource.pass_phrase if new_resource.pass_phrase
|
107
|
+
key_format[:cipher] = new_resource.cipher if new_resource.cipher
|
108
|
+
Cheffish::KeyFormatter.encode(key, key_format)
|
109
|
+
end
|
110
|
+
|
111
|
+
def write_private_key(key)
|
112
|
+
IO.write(new_resource.path, encode_private_key(key))
|
113
|
+
# TODO permissions on file?
|
114
|
+
end
|
115
|
+
|
116
|
+
def new_source_key
|
117
|
+
@new_source_key ||= begin
|
118
|
+
if new_resource.source_key.is_a?(String)
|
119
|
+
source_key, source_key_format = Cheffish::KeyFormatter.decode(new_resource.source_key, new_resource.source_key_pass_phrase)
|
120
|
+
source_key
|
121
|
+
elsif new_resource.source_key
|
122
|
+
new_resource.source_key
|
123
|
+
elsif new_resource.source_key_path
|
124
|
+
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)
|
125
|
+
source_key
|
126
|
+
else
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
attr_reader :current_private_key
|
133
|
+
|
134
|
+
def load_current_resource
|
135
|
+
resource = Chef::Resource::PrivateKey.new(new_resource.name)
|
136
|
+
|
137
|
+
if new_resource.path != :none && ::File.exist?(new_resource.path)
|
138
|
+
resource.path new_resource.path
|
139
|
+
|
140
|
+
begin
|
141
|
+
key, key_format = Cheffish::KeyFormatter.decode(IO.read(new_resource.path), new_resource.pass_phrase, new_resource.path)
|
142
|
+
if key
|
143
|
+
@current_private_key = key
|
144
|
+
resource.format key_format[:format]
|
145
|
+
resource.type key_format[:type]
|
146
|
+
resource.size key_format[:size]
|
147
|
+
resource.exponent key_format[:exponent]
|
148
|
+
resource.pass_phrase key_format[:pass_phrase]
|
149
|
+
resource.cipher key_format[:cipher]
|
150
|
+
end
|
151
|
+
rescue
|
152
|
+
# If there's an error reading, we assume format and type are wrong and don't futz with them
|
153
|
+
end
|
154
|
+
else
|
155
|
+
resource.action :delete
|
156
|
+
end
|
157
|
+
|
158
|
+
@current_resource = resource
|
159
|
+
end
|
160
|
+
end
|