cheffish 0.7.1 → 0.8
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/lib/chef/provider/chef_acl.rb +434 -0
- data/lib/chef/provider/chef_client.rb +5 -1
- data/lib/chef/provider/chef_container.rb +50 -0
- data/lib/chef/provider/chef_group.rb +78 -0
- data/lib/chef/provider/chef_mirror.rb +138 -0
- data/lib/chef/provider/chef_organization.rb +150 -0
- data/lib/chef/provider/chef_user.rb +6 -1
- data/lib/chef/provider/public_key.rb +0 -1
- data/lib/chef/resource/chef_acl.rb +38 -44
- data/lib/chef/resource/chef_container.rb +18 -0
- data/lib/chef/resource/chef_group.rb +49 -0
- data/lib/chef/resource/chef_mirror.rb +47 -0
- data/lib/chef/resource/chef_organization.rb +64 -0
- data/lib/chef/resource/private_key.rb +6 -1
- data/lib/chef/resource/public_key.rb +5 -0
- data/lib/cheffish/actor_provider_base.rb +14 -9
- data/lib/cheffish/basic_chef_client.rb +18 -2
- data/lib/cheffish/chef_provider_base.rb +7 -0
- data/lib/cheffish/merged_config.rb +10 -2
- data/lib/cheffish/recipe_dsl.rb +34 -8
- data/lib/cheffish/server_api.rb +12 -2
- data/lib/cheffish/version.rb +1 -1
- data/lib/cheffish.rb +2 -2
- data/spec/functional/merged_config_spec.rb +20 -0
- data/spec/integration/chef_acl_spec.rb +914 -0
- data/spec/integration/chef_client_spec.rb +78 -44
- data/spec/integration/chef_container_spec.rb +34 -0
- data/spec/integration/chef_group_spec.rb +324 -0
- data/spec/integration/chef_mirror_spec.rb +244 -0
- data/spec/integration/chef_node_spec.rb +115 -93
- data/spec/integration/chef_organization_spec.rb +244 -0
- data/spec/integration/chef_user_spec.rb +51 -9
- data/spec/support/repository_support.rb +103 -0
- data/spec/support/spec_support.rb +55 -2
- metadata +23 -9
- data/lib/chef/resource/in_parallel.rb +0 -6
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'chef/provider/lwrp_base'
|
2
|
+
require 'chef/chef_fs/file_pattern'
|
3
|
+
require 'chef/chef_fs/file_system'
|
4
|
+
require 'chef/chef_fs/parallelizer'
|
5
|
+
require 'chef/chef_fs/file_system/chef_server_root_dir'
|
6
|
+
require 'chef/chef_fs/file_system/chef_repository_file_system_root_dir'
|
7
|
+
|
8
|
+
class Chef::Provider::ChefMirror < Chef::Provider::LWRPBase
|
9
|
+
|
10
|
+
def whyrun_supported?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
action :upload do
|
15
|
+
copy_to(local_fs, remote_fs)
|
16
|
+
end
|
17
|
+
|
18
|
+
action :download do
|
19
|
+
copy_to(remote_fs, local_fs)
|
20
|
+
end
|
21
|
+
|
22
|
+
def copy_to(src_root, dest_root)
|
23
|
+
if new_resource.concurrency && new_resource.concurrency <= 0
|
24
|
+
raise "chef_mirror.concurrency must be above 0! Was set to #{new_resource.concurrency}"
|
25
|
+
end
|
26
|
+
# Honor concurrency
|
27
|
+
Chef::ChefFS::Parallelizer.threads = (new_resource.concurrency || 10) - 1
|
28
|
+
|
29
|
+
# We don't let the user pass absolute paths; we want to reserve those for
|
30
|
+
# multi-org support (/organizations/foo).
|
31
|
+
if new_resource.path[0] == '/'
|
32
|
+
raise "Absolute paths in chef_mirror not yet supported."
|
33
|
+
end
|
34
|
+
# Copy!
|
35
|
+
path = Chef::ChefFS::FilePattern.new("/#{new_resource.path}")
|
36
|
+
ui = CopyListener.new(self)
|
37
|
+
error = Chef::ChefFS::FileSystem.copy_to(path, src_root, dest_root, nil, options, ui, proc { |p| p.path })
|
38
|
+
|
39
|
+
if error
|
40
|
+
raise "Errors while copying:#{ui.errors.map { |e| "#{e}\n" }.join('')}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def local_fs
|
45
|
+
# If chef_repo_path is set to a string, put it in the form it usually is in
|
46
|
+
# chef config (:chef_repo_path, :node_path, etc.)
|
47
|
+
path_config = new_resource.chef_repo_path
|
48
|
+
if path_config.is_a?(Hash)
|
49
|
+
chef_repo_path = path_config.delete(:chef_repo_path)
|
50
|
+
elsif path_config
|
51
|
+
chef_repo_path = path_config
|
52
|
+
path_config = {}
|
53
|
+
else
|
54
|
+
chef_repo_path = Chef::Config.chef_repo_path
|
55
|
+
path_config = Chef::Config
|
56
|
+
end
|
57
|
+
chef_repo_path = Array(chef_repo_path).flatten
|
58
|
+
|
59
|
+
# Go through the expected object paths and figure out the local paths for each.
|
60
|
+
case repo_mode
|
61
|
+
when 'hosted_everything'
|
62
|
+
object_names = %w(acls clients cookbooks containers data_bags environments groups nodes roles)
|
63
|
+
else
|
64
|
+
object_names = %w(clients cookbooks data_bags environments nodes roles users)
|
65
|
+
end
|
66
|
+
|
67
|
+
object_paths = {}
|
68
|
+
object_names.each do |object_name|
|
69
|
+
variable_name = "#{object_name[0..-2]}_path" # cookbooks -> cookbook_path
|
70
|
+
if path_config[variable_name.to_sym]
|
71
|
+
paths = Array(path_config[variable_name.to_sym]).flatten
|
72
|
+
else
|
73
|
+
paths = chef_repo_path.map { |path| ::File.join(path, object_name) }
|
74
|
+
end
|
75
|
+
object_paths[object_name] = paths.map { |path| ::File.expand_path(path) }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Set up the root dir
|
79
|
+
Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir.new(object_paths)
|
80
|
+
end
|
81
|
+
|
82
|
+
def remote_fs
|
83
|
+
config = {
|
84
|
+
:chef_server_url => new_resource.chef_server[:chef_server_url],
|
85
|
+
:node_name => new_resource.chef_server[:options][:client_name],
|
86
|
+
:client_key => new_resource.chef_server[:options][:client_key],
|
87
|
+
:repo_mode => repo_mode
|
88
|
+
}
|
89
|
+
Chef::ChefFS::FileSystem::ChefServerRootDir.new("remote", config)
|
90
|
+
end
|
91
|
+
|
92
|
+
def repo_mode
|
93
|
+
new_resource.chef_server[:chef_server_url] =~ /\/organizations\// ? 'hosted_everything' : 'everything'
|
94
|
+
end
|
95
|
+
|
96
|
+
def options
|
97
|
+
result = {
|
98
|
+
:purge => new_resource.purge,
|
99
|
+
:freeze => new_resource.freeze,
|
100
|
+
:diff => new_resource.no_diff,
|
101
|
+
:dry_run => whyrun_mode?
|
102
|
+
}
|
103
|
+
result[:diff] = !result[:diff]
|
104
|
+
result[:repo_mode] = repo_mode
|
105
|
+
result[:concurrency] = new_resource.concurrency if new_resource.concurrency
|
106
|
+
result
|
107
|
+
end
|
108
|
+
|
109
|
+
def load_current_resource
|
110
|
+
end
|
111
|
+
|
112
|
+
class CopyListener
|
113
|
+
def initialize(mirror)
|
114
|
+
@mirror = mirror
|
115
|
+
@errors = []
|
116
|
+
end
|
117
|
+
|
118
|
+
attr_reader :mirror
|
119
|
+
attr_reader :errors
|
120
|
+
|
121
|
+
# TODO output is not *always* indicative of a change. We may want to give
|
122
|
+
# ChefFS the ability to tell us that info. For now though, assuming any output
|
123
|
+
# means change is pretty damn close to the truth.
|
124
|
+
def output(str)
|
125
|
+
mirror.converge_by str do
|
126
|
+
end
|
127
|
+
end
|
128
|
+
def warn(str)
|
129
|
+
mirror.converge_by "WARNING: #{str}" do
|
130
|
+
end
|
131
|
+
end
|
132
|
+
def error(str)
|
133
|
+
mirror.converge_by "ERROR: #{str}" do
|
134
|
+
end
|
135
|
+
@errors << str
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'cheffish/chef_provider_base'
|
2
|
+
require 'chef/resource/chef_organization'
|
3
|
+
require 'chef/chef_fs/data_handler/data_handler_base'
|
4
|
+
|
5
|
+
class Chef::Provider::ChefOrganization < 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 organization #{new_resource.name} at #{rest.url}" ] + differences
|
17
|
+
converge_by description do
|
18
|
+
rest.put("#{rest.root_url}/organizations/#{new_resource.name}", normalize_for_put(new_json))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
else
|
22
|
+
description = [ "create organization #{new_resource.name} at #{rest.url}" ] + differences
|
23
|
+
converge_by description do
|
24
|
+
rest.post("#{rest.root_url}/organizations", normalize_for_post(new_json))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Revoke invites and memberships when asked
|
29
|
+
invites_to_remove.each do |user|
|
30
|
+
if outstanding_invites.has_key?(user)
|
31
|
+
converge_by "revoke #{user}'s invitation to organization #{new_resource.name}" do
|
32
|
+
rest.delete("#{rest.root_url}/organizations/#{new_resource.name}/association_requests/#{outstanding_invites[user]}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
members_to_remove.each do |user|
|
37
|
+
if existing_members.include?(user)
|
38
|
+
converge_by "remove #{user} from organization #{new_resource.name}" do
|
39
|
+
rest.delete("#{rest.root_url}/organizations/#{new_resource.name}/users/#{user}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Invite and add members when asked
|
45
|
+
new_resource.invites.each do |user|
|
46
|
+
if !existing_members.include?(user) && !outstanding_invites.has_key?(user)
|
47
|
+
converge_by "invite #{user} to organization #{new_resource.name}" do
|
48
|
+
rest.post("#{rest.root_url}/organizations/#{new_resource.name}/association_requests", { 'user' => user })
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
new_resource.members.each do |user|
|
53
|
+
if !existing_members.include?(user)
|
54
|
+
converge_by "Add #{user} to organization #{new_resource.name}" do
|
55
|
+
rest.post("#{rest.root_url}/organizations/#{new_resource.name}/users/#{user}", {})
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def existing_members
|
62
|
+
@existing_members ||= rest.get("#{rest.root_url}/organizations/#{new_resource.name}/users").map { |u| u['user']['username'] }
|
63
|
+
end
|
64
|
+
|
65
|
+
def outstanding_invites
|
66
|
+
@outstanding_invites ||= begin
|
67
|
+
invites = {}
|
68
|
+
rest.get("#{rest.root_url}/organizations/#{new_resource.name}/association_requests").each do |r|
|
69
|
+
invites[r['username']] = r['id']
|
70
|
+
end
|
71
|
+
invites
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def invites_to_remove
|
76
|
+
if new_resource.complete
|
77
|
+
if new_resource.invites_specified? || new_resource.members_specified?
|
78
|
+
outstanding_invites.keys - (new_resource.invites | new_resource.members)
|
79
|
+
else
|
80
|
+
[]
|
81
|
+
end
|
82
|
+
else
|
83
|
+
new_resource.remove_members
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def members_to_remove
|
88
|
+
if new_resource.complete
|
89
|
+
if new_resource.members_specified?
|
90
|
+
existing_members - (new_resource.invites | new_resource.members)
|
91
|
+
else
|
92
|
+
[]
|
93
|
+
end
|
94
|
+
else
|
95
|
+
new_resource.remove_members
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
action :delete do
|
100
|
+
if current_resource_exists?
|
101
|
+
converge_by "delete organization #{new_resource.name} at #{rest.url}" do
|
102
|
+
rest.delete("#{rest.root_url}/organizations/#{new_resource.name}")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def load_current_resource
|
108
|
+
begin
|
109
|
+
@current_resource = json_to_resource(rest.get("#{rest.root_url}/organizations/#{new_resource.name}"))
|
110
|
+
rescue Net::HTTPServerException => e
|
111
|
+
if e.response.code == "404"
|
112
|
+
@current_resource = not_found_resource
|
113
|
+
else
|
114
|
+
raise
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# Helpers
|
121
|
+
#
|
122
|
+
|
123
|
+
def resource_class
|
124
|
+
Chef::Resource::ChefOrganization
|
125
|
+
end
|
126
|
+
|
127
|
+
def data_handler
|
128
|
+
OrganizationDataHandler.new
|
129
|
+
end
|
130
|
+
|
131
|
+
def keys
|
132
|
+
{
|
133
|
+
'name' => :name,
|
134
|
+
'full_name' => :full_name
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
class OrganizationDataHandler < Chef::ChefFS::DataHandler::DataHandlerBase
|
139
|
+
def normalize(organization, entry)
|
140
|
+
# Normalize the order of the keys for easier reading
|
141
|
+
normalize_hash(organization, {
|
142
|
+
'name' => remove_dot_json(entry.name),
|
143
|
+
'full_name' => remove_dot_json(entry.name),
|
144
|
+
'org_type' => 'Business',
|
145
|
+
'clientname' => "#{remove_dot_json(entry.name)}-validator",
|
146
|
+
'billing_plan' => 'platform-free'
|
147
|
+
})
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -25,6 +25,10 @@ class Chef::Provider::ChefUser < Cheffish::ActorProviderBase
|
|
25
25
|
'user'
|
26
26
|
end
|
27
27
|
|
28
|
+
def actor_path
|
29
|
+
"#{rest.root_url}/users"
|
30
|
+
end
|
31
|
+
|
28
32
|
def resource_class
|
29
33
|
Chef::Resource::ChefUser
|
30
34
|
end
|
@@ -36,6 +40,7 @@ class Chef::Provider::ChefUser < Cheffish::ActorProviderBase
|
|
36
40
|
def keys
|
37
41
|
{
|
38
42
|
'name' => :name,
|
43
|
+
'username' => :name,
|
39
44
|
'admin' => :admin,
|
40
45
|
'email' => :email,
|
41
46
|
'password' => :password,
|
@@ -45,4 +50,4 @@ class Chef::Provider::ChefUser < Cheffish::ActorProviderBase
|
|
45
50
|
}
|
46
51
|
end
|
47
52
|
|
48
|
-
end
|
53
|
+
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
require 'cheffish'
|
2
2
|
require 'chef/resource/lwrp_base'
|
3
|
-
require 'chef/environment'
|
4
3
|
|
5
4
|
class Chef::Resource::ChefAcl < Chef::Resource::LWRPBase
|
6
5
|
self.resource_name = 'chef_acl'
|
7
6
|
|
8
|
-
actions :create, :
|
7
|
+
actions :create, :nothing
|
9
8
|
default_action :create
|
10
9
|
|
11
10
|
def initialize(*args)
|
@@ -13,59 +12,54 @@ class Chef::Resource::ChefAcl < Chef::Resource::LWRPBase
|
|
13
12
|
chef_server run_context.cheffish.current_chef_server
|
14
13
|
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
}
|
21
|
-
attribute :default_attributes, :kind_of => Hash
|
22
|
-
attribute :override_attributes, :kind_of => Hash
|
15
|
+
# Path of the thing being secured, e.g. nodes, nodes/*, nodes/mynode,
|
16
|
+
# */*, **, roles/base, data/secrets, cookbooks/apache2, /users/*,
|
17
|
+
# /organizations/foo/nodes/x
|
18
|
+
attribute :path, :kind_of => String, :name_attribute => true
|
23
19
|
|
24
|
-
#
|
25
|
-
#
|
20
|
+
# Whether to change things recursively. true means it will descend all children
|
21
|
+
# and make the same modifications to them. :on_change will only descend if
|
22
|
+
# the parent has changed. :on_change is the default.
|
23
|
+
attribute :recursive, :equal_to => [ true, false, :on_change ], :default => :on_change
|
24
|
+
|
25
|
+
# Specifies that this is a complete specification for the acl (i.e. rights
|
26
|
+
# you don't specify will be reset to their defaults)
|
26
27
|
attribute :complete, :kind_of => [TrueClass, FalseClass]
|
27
28
|
|
28
29
|
attribute :raw_json, :kind_of => Hash
|
29
30
|
attribute :chef_server, :kind_of => Hash
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
# end
|
38
|
-
# default 'ip_address', :delete
|
39
|
-
attr_reader :default_attribute_modifiers
|
40
|
-
def default(attribute_path, value=NOT_PASSED, &block)
|
41
|
-
@default_attribute_modifiers ||= []
|
42
|
-
if value != NOT_PASSED
|
43
|
-
@default_attribute_modifiers << [ attribute_path, value ]
|
44
|
-
elsif block
|
45
|
-
@default_attribute_modifiers << [ attribute_path, block ]
|
32
|
+
# rights :read, :users => 'jkeiser', :groups => [ 'admins', 'users' ]
|
33
|
+
# rights [ :create, :read ], :users => [ 'jkeiser', 'adam' ]
|
34
|
+
# rights :all, :users => 'jkeiser'
|
35
|
+
def rights(*values)
|
36
|
+
if values.size == 0
|
37
|
+
@rights
|
46
38
|
else
|
47
|
-
|
39
|
+
args = values.pop
|
40
|
+
args[:permissions] ||= []
|
41
|
+
values.each do |value|
|
42
|
+
args[:permissions] |= Array(value)
|
43
|
+
end
|
44
|
+
@rights ||= []
|
45
|
+
@rights << args
|
48
46
|
end
|
49
47
|
end
|
50
48
|
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
attr_reader :override_attribute_modifiers
|
58
|
-
def override(attribute_path, value=NOT_PASSED, &block)
|
59
|
-
@override_attribute_modifiers ||= []
|
60
|
-
if value != NOT_PASSED
|
61
|
-
@override_attribute_modifiers << [ attribute_path, value ]
|
62
|
-
elsif block
|
63
|
-
@override_attribute_modifiers << [ attribute_path, block ]
|
49
|
+
# remove_rights :read, :users => 'jkeiser', :groups => [ 'admins', 'users' ]
|
50
|
+
# remove_rights [ :create, :read ], :users => [ 'jkeiser', 'adam' ]
|
51
|
+
# remove_rights :all, :users => [ 'jkeiser', 'adam' ]
|
52
|
+
def remove_rights(*values)
|
53
|
+
if values.size == 0
|
54
|
+
@remove_rights
|
64
55
|
else
|
65
|
-
|
56
|
+
args = values.pop
|
57
|
+
args[:permissions] ||= []
|
58
|
+
values.each do |value|
|
59
|
+
args[:permissions] |= Array(value)
|
60
|
+
end
|
61
|
+
@remove_rights ||= []
|
62
|
+
@remove_rights << args
|
66
63
|
end
|
67
64
|
end
|
68
|
-
|
69
|
-
alias :attributes :default_attributes
|
70
|
-
alias :attribute :default
|
71
65
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'cheffish'
|
2
|
+
require 'chef/resource/lwrp_base'
|
3
|
+
|
4
|
+
class Chef::Resource::ChefContainer < Chef::Resource::LWRPBase
|
5
|
+
self.resource_name = 'chef_container'
|
6
|
+
|
7
|
+
actions :create, :delete, :nothing
|
8
|
+
default_action :create
|
9
|
+
|
10
|
+
# Grab environment from with_environment
|
11
|
+
def initialize(*args)
|
12
|
+
super
|
13
|
+
chef_server run_context.cheffish.current_chef_server
|
14
|
+
end
|
15
|
+
|
16
|
+
attribute :name, :kind_of => String, :regex => Cheffish::NAME_REGEX, :name_attribute => true
|
17
|
+
attribute :chef_server, :kind_of => Hash
|
18
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'cheffish'
|
2
|
+
require 'chef/resource/lwrp_base'
|
3
|
+
require 'chef/run_list/run_list_item'
|
4
|
+
|
5
|
+
class Chef::Resource::ChefGroup < Chef::Resource::LWRPBase
|
6
|
+
self.resource_name = 'chef_group'
|
7
|
+
|
8
|
+
actions :create, :delete, :nothing
|
9
|
+
default_action :create
|
10
|
+
|
11
|
+
# Grab environment from with_environment
|
12
|
+
def initialize(*args)
|
13
|
+
super
|
14
|
+
chef_server run_context.cheffish.current_chef_server
|
15
|
+
@users = []
|
16
|
+
@clients = []
|
17
|
+
@groups = []
|
18
|
+
@remove_users = []
|
19
|
+
@remove_clients = []
|
20
|
+
@remove_groups = []
|
21
|
+
end
|
22
|
+
|
23
|
+
attribute :name, :kind_of => String, :regex => Cheffish::NAME_REGEX, :name_attribute => true
|
24
|
+
def users(*users)
|
25
|
+
users.size == 0 ? @users : (@users |= users.flatten)
|
26
|
+
end
|
27
|
+
def clients(*clients)
|
28
|
+
clients.size == 0 ? @clients : (@clients |= clients.flatten)
|
29
|
+
end
|
30
|
+
def groups(*groups)
|
31
|
+
groups.size == 0 ? @groups : (@groups |= groups.flatten)
|
32
|
+
end
|
33
|
+
def remove_users(*remove_users)
|
34
|
+
remove_users.size == 0 ? @remove_users : (@remove_users |= remove_users.flatten)
|
35
|
+
end
|
36
|
+
def remove_clients(*remove_clients)
|
37
|
+
remove_clients.size == 0 ? @remove_clients : (@remove_clients |= remove_clients.flatten)
|
38
|
+
end
|
39
|
+
def remove_groups(*remove_groups)
|
40
|
+
remove_groups.size == 0 ? @remove_groups : (@remove_groups |= remove_groups.flatten)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Specifies that this is a complete specification for the environment (i.e. attributes you don't specify will be
|
44
|
+
# reset to their defaults)
|
45
|
+
attribute :complete, :kind_of => [TrueClass, FalseClass]
|
46
|
+
|
47
|
+
attribute :raw_json, :kind_of => Hash
|
48
|
+
attribute :chef_server, :kind_of => Hash
|
49
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'cheffish'
|
2
|
+
require 'chef/resource/lwrp_base'
|
3
|
+
|
4
|
+
class Chef::Resource::ChefMirror < Chef::Resource::LWRPBase
|
5
|
+
self.resource_name = 'chef_mirror'
|
6
|
+
|
7
|
+
actions :upload, :download, :nothing
|
8
|
+
default_action :nothing
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
super
|
12
|
+
chef_server run_context.cheffish.current_chef_server
|
13
|
+
end
|
14
|
+
|
15
|
+
# Path of the data to mirror, e.g. nodes, nodes/*, nodes/mynode,
|
16
|
+
# */*, **, roles/base, data/secrets, cookbooks/apache2, etc.
|
17
|
+
attribute :path, :kind_of => String, :name_attribute => true
|
18
|
+
|
19
|
+
# Local path. Can be a string (top level of repository) or hash
|
20
|
+
# (:chef_repo_path, :node_path, etc.)
|
21
|
+
# If neither chef_repo_path nor versioned_cookbooks are set, they default to their
|
22
|
+
# Chef::Config values. If chef_repo_path is set but versioned_cookbooks is not,
|
23
|
+
# versioned_cookbooks defaults to true.
|
24
|
+
attribute :chef_repo_path, :kind_of => [ String, Hash ]
|
25
|
+
|
26
|
+
# Whether the repo path contains / should contain cookbooks with versioned names,
|
27
|
+
# i.e. cookbooks/mysql-1.0.0, cookbooks/mysql-1.2.0, etc.
|
28
|
+
attribute :versioned_cookbooks, :kind_of => [ TrueClass, FalseClass ]
|
29
|
+
|
30
|
+
# Chef server
|
31
|
+
attribute :chef_server, :kind_of => Hash
|
32
|
+
|
33
|
+
# Whether to purge deleted things: if we do not have cookbooks/x locally and we
|
34
|
+
# *do* have cookbooks/x remotely, then :upload with purge will delete it.
|
35
|
+
# Defaults to false.
|
36
|
+
attribute :purge, :kind_of => [ TrueClass, FalseClass ]
|
37
|
+
|
38
|
+
# Whether to freeze cookbooks on upload
|
39
|
+
attribute :freeze, :kind_of => [ TrueClass, FalseClass ]
|
40
|
+
|
41
|
+
# If this is true, only new files will be copied. File contents will not be
|
42
|
+
# diffed, so changed files will never be uploaded.
|
43
|
+
attribute :no_diff, :kind_of => [ TrueClass, FalseClass ]
|
44
|
+
|
45
|
+
# Number of parallel threads to list/upload/download with. Defaults to 10.
|
46
|
+
attribute :concurrency, :kind_of => Integer
|
47
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'cheffish'
|
2
|
+
require 'chef/resource/lwrp_base'
|
3
|
+
require 'chef/run_list/run_list_item'
|
4
|
+
|
5
|
+
class Chef::Resource::ChefOrganization < Chef::Resource::LWRPBase
|
6
|
+
self.resource_name = 'chef_organization'
|
7
|
+
|
8
|
+
actions :create, :delete, :nothing
|
9
|
+
default_action :create
|
10
|
+
|
11
|
+
# Grab environment from with_environment
|
12
|
+
def initialize(*args)
|
13
|
+
super
|
14
|
+
chef_server run_context.cheffish.current_chef_server
|
15
|
+
@invites = nil
|
16
|
+
@members = nil
|
17
|
+
@remove_members = []
|
18
|
+
end
|
19
|
+
|
20
|
+
attribute :name, :kind_of => String, :regex => Cheffish::NAME_REGEX, :name_attribute => true
|
21
|
+
attribute :full_name, :kind_of => String
|
22
|
+
|
23
|
+
# A list of users who must at least be invited to the org (but may already be
|
24
|
+
# members). Invites will be sent to users who are not already invited/in the org.
|
25
|
+
def invites(*users)
|
26
|
+
if users.size == 0
|
27
|
+
@invites || []
|
28
|
+
else
|
29
|
+
@invites ||= []
|
30
|
+
@invites |= users.flatten
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def invites_specified?
|
35
|
+
!!@invites
|
36
|
+
end
|
37
|
+
|
38
|
+
# A list of users who must be members of the org. This will use the new Chef 12
|
39
|
+
# POST /organizations/ORG/users/NAME endpoint to add them directly to the org.
|
40
|
+
# If you do not have permission to perform this operation, and the users are not
|
41
|
+
# a part of the org, the resource update will fail.
|
42
|
+
def members(*users)
|
43
|
+
if users.size == 0
|
44
|
+
@members || []
|
45
|
+
else
|
46
|
+
@members ||= []
|
47
|
+
@members |= users.flatten
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def members_specified?
|
52
|
+
!!@members
|
53
|
+
end
|
54
|
+
|
55
|
+
# A list of users who must not be members of the org. These users will be removed
|
56
|
+
# from the org and invites will be revoked (if any).
|
57
|
+
def remove_members(*users)
|
58
|
+
users.size == 0 ? @remove_members : (@remove_members |= users.flatten)
|
59
|
+
end
|
60
|
+
|
61
|
+
attribute :complete, :kind_of => [ TrueClass, FalseClass ]
|
62
|
+
attribute :raw_json, :kind_of => Hash
|
63
|
+
attribute :chef_server, :kind_of => Hash
|
64
|
+
end
|
@@ -36,4 +36,9 @@ class Chef::Resource::PrivateKey < Chef::Resource::LWRPBase
|
|
36
36
|
def after(&block)
|
37
37
|
block ? @after = block : @after
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
|
+
# We are not interested in Chef's cloning behavior here.
|
41
|
+
def load_prior_resource
|
42
|
+
Chef::Log.debug("Overloading #{resource_name}.load_prior_resource with NOOP")
|
43
|
+
end
|
44
|
+
end
|
@@ -13,4 +13,9 @@ class Chef::Resource::PublicKey < Chef::Resource::LWRPBase
|
|
13
13
|
attribute :source_key
|
14
14
|
attribute :source_key_path, :kind_of => String
|
15
15
|
attribute :source_key_pass_phrase
|
16
|
+
|
17
|
+
# We are not interested in Chef's cloning behavior here.
|
18
|
+
def load_prior_resource
|
19
|
+
Chef::Log.debug("Overloading #{resource_name}.load_prior_resource with NOOP")
|
20
|
+
end
|
16
21
|
end
|