cheffish 0.7.1 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/chef/provider/chef_acl.rb +434 -0
  3. data/lib/chef/provider/chef_client.rb +5 -1
  4. data/lib/chef/provider/chef_container.rb +50 -0
  5. data/lib/chef/provider/chef_group.rb +78 -0
  6. data/lib/chef/provider/chef_mirror.rb +138 -0
  7. data/lib/chef/provider/chef_organization.rb +150 -0
  8. data/lib/chef/provider/chef_user.rb +6 -1
  9. data/lib/chef/provider/public_key.rb +0 -1
  10. data/lib/chef/resource/chef_acl.rb +38 -44
  11. data/lib/chef/resource/chef_container.rb +18 -0
  12. data/lib/chef/resource/chef_group.rb +49 -0
  13. data/lib/chef/resource/chef_mirror.rb +47 -0
  14. data/lib/chef/resource/chef_organization.rb +64 -0
  15. data/lib/chef/resource/private_key.rb +6 -1
  16. data/lib/chef/resource/public_key.rb +5 -0
  17. data/lib/cheffish/actor_provider_base.rb +14 -9
  18. data/lib/cheffish/basic_chef_client.rb +18 -2
  19. data/lib/cheffish/chef_provider_base.rb +7 -0
  20. data/lib/cheffish/merged_config.rb +10 -2
  21. data/lib/cheffish/recipe_dsl.rb +34 -8
  22. data/lib/cheffish/server_api.rb +12 -2
  23. data/lib/cheffish/version.rb +1 -1
  24. data/lib/cheffish.rb +2 -2
  25. data/spec/functional/merged_config_spec.rb +20 -0
  26. data/spec/integration/chef_acl_spec.rb +914 -0
  27. data/spec/integration/chef_client_spec.rb +78 -44
  28. data/spec/integration/chef_container_spec.rb +34 -0
  29. data/spec/integration/chef_group_spec.rb +324 -0
  30. data/spec/integration/chef_mirror_spec.rb +244 -0
  31. data/spec/integration/chef_node_spec.rb +115 -93
  32. data/spec/integration/chef_organization_spec.rb +244 -0
  33. data/spec/integration/chef_user_spec.rb +51 -9
  34. data/spec/support/repository_support.rb +103 -0
  35. data/spec/support/spec_support.rb +55 -2
  36. metadata +23 -9
  37. data/lib/chef/resource/in_parallel.rb +0 -6
@@ -14,9 +14,9 @@ class Cheffish::ActorProviderBase < Cheffish::ChefProviderBase
14
14
  if current_resource_exists?
15
15
  # Update the actor if it's different
16
16
  if differences.size > 0
17
- description = [ "update #{actor_type} #{new_resource.name} at #{rest.url}" ] + differences
17
+ description = [ "update #{actor_type} #{new_resource.name} at #{actor_path}" ] + differences
18
18
  converge_by description do
19
- result = rest.put("#{actor_type}s/#{new_resource.name}", normalize_for_put(new_json))
19
+ result = rest.put("#{actor_path}/#{new_resource.name}", normalize_for_put(new_json))
20
20
  current_public_key, current_public_key_format = Cheffish::KeyFormatter.decode(result['public_key']) if result['public_key']
21
21
  end
22
22
  end
@@ -25,9 +25,9 @@ class Cheffish::ActorProviderBase < Cheffish::ChefProviderBase
25
25
  if !new_public_key
26
26
  raise "You must specify a public key to create a #{actor_type}! Use the private_key resource to create a key, and pass it in with source_key_path."
27
27
  end
28
- description = [ "create #{actor_type} #{new_resource.name} at #{rest.url}" ] + differences
28
+ description = [ "create #{actor_type} #{new_resource.name} at #{actor_path}" ] + differences
29
29
  converge_by description do
30
- result = rest.post("#{actor_type}s", normalize_for_post(new_json))
30
+ result = rest.post("#{actor_path}", normalize_for_post(new_json))
31
31
  current_public_key, current_public_key_format = Cheffish::KeyFormatter.decode(result['public_key']) if result['public_key']
32
32
  end
33
33
  end
@@ -58,8 +58,8 @@ class Cheffish::ActorProviderBase < Cheffish::ChefProviderBase
58
58
 
59
59
  def delete_actor
60
60
  if current_resource_exists?
61
- converge_by "delete #{actor_type} #{new_resource.name} at #{rest.url}" do
62
- rest.delete("#{actor_type}s/#{new_resource.name}")
61
+ converge_by "delete #{actor_type} #{new_resource.name} at #{actor_path}" do
62
+ rest.delete("#{actor_path}/#{new_resource.name}")
63
63
  Chef::Log.info("#{new_resource} deleted #{actor_type} #{new_resource.name} at #{rest.url}")
64
64
  end
65
65
  end
@@ -74,7 +74,13 @@ class Cheffish::ActorProviderBase < Cheffish::ChefProviderBase
74
74
  @new_public_key ||= begin
75
75
  if new_resource.source_key
76
76
  if new_resource.source_key.is_a?(String)
77
- Cheffish::KeyFormatter.decode(new_resource.source_key)
77
+ key, key_format = Cheffish::KeyFormatter.decode(new_resource.source_key)
78
+
79
+ if key.private?
80
+ key.public_key
81
+ else
82
+ key
83
+ end
78
84
  elsif new_resource.source_key.private?
79
85
  new_resource.source_key.public_key
80
86
  else
@@ -108,7 +114,7 @@ class Cheffish::ActorProviderBase < Cheffish::ChefProviderBase
108
114
 
109
115
  def load_current_resource
110
116
  begin
111
- json = rest.get("#{actor_type}s/#{new_resource.name}")
117
+ json = rest.get("#{actor_path}/#{new_resource.name}")
112
118
  @current_resource = json_to_resource(json)
113
119
  rescue Net::HTTPServerException => e
114
120
  if e.response.code == "404"
@@ -122,5 +128,4 @@ class Cheffish::ActorProviderBase < Cheffish::ChefProviderBase
122
128
  current_resource.output_key_path = new_resource.output_key_path
123
129
  end
124
130
  end
125
-
126
131
  end
@@ -37,6 +37,11 @@ module Cheffish
37
37
  attr_accessor :recipe_name
38
38
  def_delegators :@run_context, :resource_collection, :immediate_notifications, :delayed_notifications
39
39
 
40
+ def add_resource(resource)
41
+ resource.run_context = run_context
42
+ run_context.resource_collection.insert(resource)
43
+ end
44
+
40
45
  def load_block(&block)
41
46
  @recipe_name = 'block'
42
47
  instance_eval(&block)
@@ -54,10 +59,21 @@ module Cheffish
54
59
  @event_catcher.updates.size > 0
55
60
  end
56
61
 
57
- def self.inline_resource(provider, provider_action, &block)
62
+ # Builds a resource sans context, which can be later used in a new client's
63
+ # add_resource() method.
64
+ def self.build_resource(type, name, created_at=nil, &resource_attrs_block)
65
+ created_at ||= caller[0]
66
+ result = BasicChefClient.new.build_resource(type, name, created_at, &resource_attrs_block)
67
+ result
68
+ end
69
+
70
+ def self.inline_resource(provider, provider_action, *resources, &block)
58
71
  events = ProviderEventForwarder.new(provider, provider_action)
59
72
  client = BasicChefClient.new(provider.node, events)
60
- client.load_block(&block)
73
+ resources.each do |resource|
74
+ client.add_resource(resource)
75
+ end
76
+ client.load_block(&block) if block
61
77
  client.converge
62
78
  client.updated?
63
79
  end
@@ -215,10 +215,17 @@ module Cheffish
215
215
  def initialize(name, parent = nil)
216
216
  @name = name
217
217
  @parent = parent
218
+ @org = nil
218
219
  end
219
220
 
220
221
  attr_reader :name
221
222
  attr_reader :parent
223
+ attr_reader :org
222
224
  end
223
225
  end
226
+
227
+ # We are not interested in Chef's cloning behavior here.
228
+ def load_prior_resource
229
+ Chef::Log.debug("Overloading #{resource_name}.load_prior_resource with NOOP")
230
+ end
224
231
  end
@@ -45,8 +45,12 @@ module Cheffish
45
45
  end
46
46
  end
47
47
 
48
- def method_missing(name)
49
- self[name]
48
+ def method_missing(name, *args)
49
+ if args.count > 0
50
+ raise NoMethodError, "Unexpected method #{name} for MergedConfig with arguments #{args}"
51
+ else
52
+ self[name]
53
+ end
50
54
  end
51
55
 
52
56
  def key?(name)
@@ -82,5 +86,9 @@ module Cheffish
82
86
  end
83
87
  result
84
88
  end
89
+
90
+ def to_s
91
+ to_hash.to_s
92
+ end
85
93
  end
86
94
  end
@@ -1,5 +1,6 @@
1
1
  require 'cheffish'
2
2
 
3
+ require 'chef/version'
3
4
  require 'chef_zero/server'
4
5
  require 'chef/chef_fs/chef_fs_data_store'
5
6
  require 'chef/chef_fs/config'
@@ -7,26 +8,38 @@ require 'cheffish/chef_run_data'
7
8
  require 'cheffish/chef_run_listener'
8
9
  require 'chef/client'
9
10
  require 'chef/config'
11
+ require 'chef_zero/version'
10
12
  require 'cheffish/merged_config'
13
+ require 'chef/resource/chef_acl'
11
14
  require 'chef/resource/chef_client'
15
+ require 'chef/resource/chef_container'
12
16
  require 'chef/resource/chef_data_bag'
13
17
  require 'chef/resource/chef_data_bag_item'
14
18
  require 'chef/resource/chef_environment'
19
+ require 'chef/resource/chef_group'
20
+ require 'chef/resource/chef_mirror'
15
21
  require 'chef/resource/chef_node'
22
+ require 'chef/resource/chef_organization'
16
23
  require 'chef/resource/chef_role'
17
24
  require 'chef/resource/chef_user'
18
25
  require 'chef/resource/private_key'
19
26
  require 'chef/resource/public_key'
27
+ require 'chef/provider/chef_acl'
20
28
  require 'chef/provider/chef_client'
29
+ require 'chef/provider/chef_container'
21
30
  require 'chef/provider/chef_data_bag'
22
31
  require 'chef/provider/chef_data_bag_item'
23
32
  require 'chef/provider/chef_environment'
33
+ require 'chef/provider/chef_group'
34
+ require 'chef/provider/chef_mirror'
24
35
  require 'chef/provider/chef_node'
36
+ require 'chef/provider/chef_organization'
25
37
  require 'chef/provider/chef_role'
26
38
  require 'chef/provider/chef_user'
27
39
  require 'chef/provider/private_key'
28
40
  require 'chef/provider/public_key'
29
41
 
42
+
30
43
  class Chef
31
44
  module DSL
32
45
  module Recipe
@@ -49,7 +62,7 @@ class Chef
49
62
  def with_chef_local_server(options, &block)
50
63
  options[:host] ||= '127.0.0.1'
51
64
  options[:log_level] ||= Chef::Log.level
52
- options[:port] ||= 8901.upto(9900)
65
+ options[:port] ||= ChefZero::VERSION.to_f >= 2.2 ? 8901.upto(9900) : 8901
53
66
 
54
67
  # Create the data store chef-zero will use
55
68
  options[:data_store] ||= begin
@@ -59,16 +72,13 @@ class Chef
59
72
 
60
73
  # Ensure all paths are given
61
74
  %w(acl client cookbook container data_bag environment group node role).each do |type|
62
- options["#{type}_path".to_sym] ||= begin
75
+ options["#{type}_path"] ||= begin
63
76
  if options[:chef_repo_path].kind_of?(String)
64
- run_context.config.path_join(options[:chef_repo_path], "#{type}s")
77
+ Chef::Util::PathHelper.join(options[:chef_repo_path], "#{type}s")
65
78
  else
66
- options[:chef_repo_path].map { |path| run_context.config.path_join(path, "#{type}s")}
79
+ options[:chef_repo_path].map { |path| Chef::Util::PathHelper.join(path, "#{type}s")}
67
80
  end
68
81
  end
69
- # Work around issue in earlier versions of ChefFS where it expects strings for these
70
- # instead of symbols
71
- options["#{type}_path"] = options["#{type}_path".to_sym]
72
82
  end
73
83
 
74
84
  chef_fs = Chef::ChefFS::Config.new(options).local_fs
@@ -95,7 +105,7 @@ class Chef
95
105
  class Config
96
106
  default(:profile) { ENV['CHEF_PROFILE'] || 'default' }
97
107
  configurable(:private_keys)
98
- default(:private_key_paths) { [ path_join(config_dir, 'keys'), path_join(user_home, '.ssh') ] }
108
+ default(:private_key_paths) { [ Chef::Util::PathHelper.join(config_dir, 'keys'), Chef::Util::PathHelper.join(user_home, '.ssh') ] }
99
109
  default(:private_key_write_path) { private_key_paths.first }
100
110
  end
101
111
 
@@ -119,3 +129,19 @@ class Chef
119
129
  end
120
130
 
121
131
  end
132
+
133
+ # Chef 12 moved Chef::Config.path_join to PathHelper.join
134
+ if Chef::VERSION.to_i >= 12
135
+ require 'chef/util/path_helper'
136
+ else
137
+ require 'chef/config'
138
+ class Chef
139
+ class Util
140
+ class PathHelper
141
+ def join(*args)
142
+ Chef::Config.path_join(*args)
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -16,13 +16,16 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
+ require 'chef/version'
19
20
  require 'chef/http'
20
21
  require 'chef/http/authenticator'
21
22
  require 'chef/http/cookie_manager'
22
23
  require 'chef/http/decompressor'
23
24
  require 'chef/http/json_input'
24
25
  require 'chef/http/json_output'
25
- require 'chef/http/remote_request_id'
26
+ if Gem::Version.new(Chef::VERSION) >= Gem::Version.new('11.12')
27
+ require 'chef/http/remote_request_id'
28
+ end
26
29
 
27
30
  module Cheffish
28
31
  # Exactly like Chef::ServerAPI, but requires you to pass in what keys you want (no defaults)
@@ -30,13 +33,20 @@ module Cheffish
30
33
 
31
34
  def initialize(url, options = {})
32
35
  super(url, options)
36
+ root_url = URI.parse(url)
37
+ root_url.path = ''
38
+ @root_url = root_url.to_s
33
39
  end
34
40
 
41
+ attr_reader :root_url
42
+
35
43
  use Chef::HTTP::JSONInput
36
44
  use Chef::HTTP::JSONOutput
37
45
  use Chef::HTTP::CookieManager
38
46
  use Chef::HTTP::Decompressor
39
47
  use Chef::HTTP::Authenticator
40
- use Chef::HTTP::RemoteRequestID
48
+ if Gem::Version.new(Chef::VERSION) >= Gem::Version.new('11.12')
49
+ use Chef::HTTP::RemoteRequestID
50
+ end
41
51
  end
42
52
  end
@@ -1,3 +1,3 @@
1
1
  module Cheffish
2
- VERSION = '0.7.1'
2
+ VERSION = '0.8'
3
3
  end
data/lib/cheffish.rb CHANGED
@@ -9,8 +9,8 @@ require 'chef/application'
9
9
  module Cheffish
10
10
  NAME_REGEX = /^[.\-[:alnum:]_]+$/
11
11
 
12
- def self.inline_resource(provider, provider_action, &block)
13
- BasicChefClient.inline_resource(provider, provider_action, &block)
12
+ def self.inline_resource(provider, provider_action, *resources, &block)
13
+ BasicChefClient.inline_resource(provider, provider_action, *resources, &block)
14
14
  end
15
15
 
16
16
  def self.default_chef_server(config = profiled_config)
@@ -0,0 +1,20 @@
1
+ require 'cheffish/merged_config'
2
+
3
+ describe "merged_config" do
4
+
5
+ let(:config) do
6
+ Cheffish::MergedConfig.new({:test=>'val'})
7
+ end
8
+
9
+ it "returns value in config" do
10
+ expect(config.test).to eq('val')
11
+ end
12
+
13
+ it "raises a NoMethodError if calling an unknown method with arguments" do
14
+ expect{config.merge({:some => 'hash'})}.to raise_error(NoMethodError)
15
+ end
16
+
17
+ it "has an informative string representation" do
18
+ expect("#{config}").to eq("{:test=>\"val\"}")
19
+ end
20
+ end