cheffish 1.0.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9282dabe78e504320b3d0d7fe7bbbe419ed6a329
4
- data.tar.gz: e05972ef92c0a308e2f464e44dd8a9bac2985ec3
3
+ metadata.gz: ced2867dd0a796aadfc6b7506df7c44127a68898
4
+ data.tar.gz: dc937be5eea01310182fb3047aea34f8dbb64986
5
5
  SHA512:
6
- metadata.gz: baf25dd92324945d9bb3d927e352568a5dd23a3dccb61a01b4110d8c19f3486042bad07280b7816b1f3932239f5f8be793f882932d757c1755394023711b69f0
7
- data.tar.gz: 0fa050079ef32883732a8fd059eab45de29738ea5108fd0ab35f3da2c1f6c079b4379f485aca1eace2187cc9cb8eb9ca2e41bb3c16b0baf3d76fe477d463dcf2
6
+ metadata.gz: 05a37dbb77e8caf69b80dc3f17010a893d1f6775c19f26e2bd44ceb51e9219c61d9f294b3cea2349e9d6393de08d2a84ee2f222ce43264689f7a5b9407e9f647
7
+ data.tar.gz: 568d7edf9950890bcfa9d28dedac830bc4bebf9972e9f0d838c9692045330d9158b2d341e55c58158d47cc0c42214379258d239aa6825987909287f70907a585
@@ -209,6 +209,7 @@ class Chef::Provider::PrivateKey < Chef::Provider::LWRPBase
209
209
  end
210
210
  rescue
211
211
  # If there's an error reading, we assume format and type are wrong and don't futz with them
212
+ Chef::Log.warn("Error reading #{new_path}: #{$!}")
212
213
  end
213
214
  else
214
215
  resource.action :delete
data/lib/cheffish.rb CHANGED
@@ -1,11 +1,3 @@
1
- require 'chef/run_list/run_list_item'
2
- require 'cheffish/basic_chef_client'
3
- require 'cheffish/server_api'
4
- require 'chef/knife'
5
- require 'chef/config_fetcher'
6
- require 'chef/log'
7
- require 'chef/application'
8
-
9
1
  module Cheffish
10
2
  NAME_REGEX = /^[.\-[:alnum:]_]+$/
11
3
 
@@ -92,8 +84,10 @@ module Cheffish
92
84
  def self.get_private_key_with_path(name, config = profiled_config)
93
85
  if config[:private_keys] && config[:private_keys][name]
94
86
  if config[:private_keys][name].is_a?(String)
87
+ Chef::Log.info("Got key #{name} from Chef::Config.private_keys.#{name}, which points at #{config[:private_keys[name]]}. Reading key from there ...")
95
88
  return [ IO.read(config[:private_keys][name]), config[:private_keys][name] ]
96
89
  else
90
+ Chef::Log.info("Got key #{name} raw from Chef::Config.private_keys.#{name}.")
97
91
  return [ config[:private_keys][name].to_pem, nil ]
98
92
  end
99
93
  elsif config[:private_key_paths]
@@ -104,6 +98,7 @@ module Cheffish
104
98
  if ext == '' || ext == '.pem'
105
99
  key_name = key[0..-(ext.length+1)]
106
100
  if key_name == name
101
+ Chef::Log.info("Reading key #{name} from file #{private_key_path}/#{key}")
107
102
  return [ IO.read("#{private_key_path}/#{key}"), "#{private_key_path}/#{key}" ]
108
103
  end
109
104
  end
@@ -223,4 +218,11 @@ end
223
218
 
224
219
  # Include all recipe objects so require 'cheffish' brings in the whole recipe DSL
225
220
 
221
+ require 'chef/run_list/run_list_item'
222
+ require 'cheffish/basic_chef_client'
223
+ require 'cheffish/server_api'
224
+ require 'chef/knife'
225
+ require 'chef/config_fetcher'
226
+ require 'chef/log'
227
+ require 'chef/application'
226
228
  require 'cheffish/recipe_dsl'
@@ -13,7 +13,7 @@ module Cheffish
13
13
  class BasicChefClient
14
14
  include Chef::DSL::Recipe
15
15
 
16
- def initialize(node = nil, events = nil)
16
+ def initialize(node = nil, events = nil, **chef_config)
17
17
  if !node
18
18
  node = Chef::Node.new
19
19
  node.name 'basic_chef_client'
@@ -21,34 +21,53 @@ module Cheffish
21
21
  node.automatic[:platform_version] = Cheffish::VERSION
22
22
  end
23
23
 
24
- @event_catcher = BasicChefClientEvents.new
25
- dispatcher = Chef::EventDispatch::Dispatcher.new(@event_catcher)
26
- dispatcher.register(events) if events
27
- @run_context = Chef::RunContext.new(node, {}, dispatcher)
28
- @updated = []
29
- @cookbook_name = 'basic_chef_client'
24
+ # Decide on the config we want for this chef client
25
+ @chef_config = chef_config
26
+
27
+ with_chef_config do
28
+ @cookbook_name = 'basic_chef_client'
29
+ @event_catcher = BasicChefClientEvents.new
30
+ dispatcher = Chef::EventDispatch::Dispatcher.new(@event_catcher)
31
+ case events
32
+ when nil
33
+ when Array
34
+ events.each { |e| dispatcher.register(e) } if events
35
+ else
36
+ dispatcher.register(events)
37
+ end
38
+ @run_context = Chef::RunContext.new(node, {}, dispatcher)
39
+ @updated = []
40
+ @cookbook_name = 'basic_chef_client'
41
+ end
30
42
  end
31
43
 
32
44
  extend Forwardable
33
45
 
34
46
  # Stuff recipes need
47
+ attr_reader :chef_config
35
48
  attr_reader :run_context
36
49
  attr_accessor :cookbook_name
37
50
  attr_accessor :recipe_name
38
51
  def_delegators :@run_context, :resource_collection, :immediate_notifications, :delayed_notifications
39
52
 
40
53
  def add_resource(resource)
41
- resource.run_context = run_context
42
- run_context.resource_collection.insert(resource)
54
+ with_chef_config do
55
+ resource.run_context = run_context
56
+ run_context.resource_collection.insert(resource)
57
+ end
43
58
  end
44
59
 
45
60
  def load_block(&block)
46
- @recipe_name = 'block'
47
- instance_eval(&block)
61
+ with_chef_config do
62
+ @recipe_name = 'block'
63
+ instance_eval(&block)
64
+ end
48
65
  end
49
66
 
50
67
  def converge
51
- Chef::Runner.new(self).converge
68
+ with_chef_config do
69
+ Chef::Runner.new(self).converge
70
+ end
52
71
  end
53
72
 
54
73
  def updates
@@ -63,15 +82,21 @@ module Cheffish
63
82
  # add_resource() method.
64
83
  def self.build_resource(type, name, created_at=nil, &resource_attrs_block)
65
84
  created_at ||= caller[0]
66
- result = BasicChefClient.new.build_resource(type, name, created_at, &resource_attrs_block)
85
+ result = BasicChefClient.new.tap do |client|
86
+ client.with_chef_config do
87
+ client.build_resource(type, name, created_at, &resource_attrs_block)
88
+ end
89
+ end
67
90
  result
68
91
  end
69
92
 
70
93
  def self.inline_resource(provider, provider_action, *resources, &block)
71
94
  events = ProviderEventForwarder.new(provider, provider_action)
72
95
  client = BasicChefClient.new(provider.node, events)
73
- resources.each do |resource|
74
- client.add_resource(resource)
96
+ client.with_chef_config do
97
+ resources.each do |resource|
98
+ client.add_resource(resource)
99
+ end
75
100
  end
76
101
  client.load_block(&block) if block
77
102
  client.converge
@@ -85,6 +110,50 @@ module Cheffish
85
110
  client.updated?
86
111
  end
87
112
 
113
+ def with_chef_config(&block)
114
+ old_chef_config = Chef::Config.save
115
+ if chef_config[:log_location]
116
+ old_loggers = Chef::Log.loggers
117
+ Chef::Log.init(chef_config[:log_location])
118
+ end
119
+ if chef_config[:log_level]
120
+ old_level = Chef::Log.level
121
+ Chef::Log.level(chef_config[:log_level])
122
+ end
123
+ # if chef_config[:stdout]
124
+ # old_stdout = $stdout
125
+ # $stdout = chef_config[:stdout]
126
+ # end
127
+ # if chef_config[:stderr]
128
+ # old_stderr = $stderr
129
+ # $stderr = chef_config[:stderr]
130
+ # end
131
+ begin
132
+ deep_merge_config(chef_config, Chef::Config)
133
+ block.call
134
+ ensure
135
+ # $stdout = old_stdout if chef_config[:stdout]
136
+ # $stderr = old_stderr if chef_config[:stderr]
137
+ if old_loggers
138
+ Chef::Log.logger = old_loggers.shift
139
+ old_loggers.each { |l| Chef::Log.loggers.push(l) }
140
+ elsif chef_config[:log_level]
141
+ Chef::Log.level = old_level
142
+ end
143
+ Chef::Config.restore(old_chef_config)
144
+ end
145
+ end
146
+
147
+ def deep_merge_config(src, dest)
148
+ src.each do |name, value|
149
+ if value.is_a?(Hash) && dest[name].is_a?(Hash)
150
+ deep_merge_config(value, dest[name])
151
+ else
152
+ dest[name] = value
153
+ end
154
+ end
155
+ end
156
+
88
157
  class BasicChefClientEvents < Chef::EventDispatch::Base
89
158
  def initialize
90
159
  @updates = []
@@ -0,0 +1,142 @@
1
+ require 'cheffish/basic_chef_client'
2
+
3
+ module Cheffish
4
+ class ChefRun
5
+ #
6
+ # @param chef_config A hash with symbol keys that looks suspiciously similar to `Chef::Config`.
7
+ # Some possible options:
8
+ # - stdout: <IO object> - where to stream stdout to
9
+ # - stderr: <IO object> - where to stream stderr to
10
+ # - log_level: :debug|:info|:warn|:error|:fatal
11
+ # - log_location: <path|IO object> - where to stream logs to
12
+ # - verbose_logging: true|false - true if you want verbose logging in :debug
13
+ #
14
+ def initialize(chef_config={})
15
+ @chef_config = chef_config || {}
16
+ end
17
+
18
+ attr_reader :chef_config
19
+
20
+ class StringIOTee < StringIO
21
+ def initialize(*streams)
22
+ super()
23
+ @streams = streams.flatten.select { |s| !s.nil? }
24
+ end
25
+
26
+ attr_reader :streams
27
+
28
+ def write(*args, &block)
29
+ super
30
+ streams.each { |s| s.write(*args, &block) }
31
+ end
32
+ end
33
+
34
+ def client
35
+ @client ||= begin
36
+ chef_config = self.chef_config.dup
37
+ chef_config[:log_level] ||= :debug if !chef_config.has_key?(:log_level)
38
+ chef_config[:verbose_logging] = false if !chef_config.has_key?(:verbose_logging)
39
+ chef_config[:stdout] = StringIOTee.new(chef_config[:stdout])
40
+ chef_config[:stderr] = StringIOTee.new(chef_config[:stderr])
41
+ chef_config[:log_location] = StringIOTee.new(chef_config[:log_location])
42
+ @client = ::Cheffish::BasicChefClient.new(nil,
43
+ [ event_sink, Chef::Formatters.new(:doc, chef_config[:stdout], chef_config[:stderr]) ],
44
+ chef_config
45
+ )
46
+ end
47
+ end
48
+
49
+ def event_sink
50
+ @event_sink ||= EventSink.new
51
+ end
52
+
53
+ #
54
+ # output
55
+ #
56
+ def stdout
57
+ @client ? client.chef_config[:stdout].string : nil
58
+ end
59
+ def stderr
60
+ @client ? client.chef_config[:stderr].string : nil
61
+ end
62
+ def logs
63
+ @client ? client.chef_config[:log_location].string : nil
64
+ end
65
+
66
+ def resources
67
+ client.resource_collection
68
+ end
69
+
70
+ def converge
71
+ begin
72
+ client.converge
73
+ @converged = true
74
+ rescue RuntimeError => e
75
+ @raised_exception = e
76
+ raise
77
+ end
78
+ end
79
+
80
+ def reset
81
+ @client = nil
82
+ @converged = nil
83
+ @stdout = nil
84
+ @stderr = nil
85
+ @logs = nil
86
+ @raised_exception = nil
87
+ end
88
+
89
+ def converged?
90
+ !!@converged
91
+ end
92
+
93
+ def converge_failed?
94
+ @raised_exception.nil? ? false : true
95
+ end
96
+
97
+ def updated?
98
+ client.updated?
99
+ end
100
+
101
+ def up_to_date?
102
+ !client.updated?
103
+ end
104
+
105
+ def output_for_failure_message
106
+ message = ""
107
+ if stdout && !stdout.empty?
108
+ message << "--- ---\n"
109
+ message << "--- Chef Client Output ---\n"
110
+ message << "--- ---\n"
111
+ message << stdout
112
+ message << "\n" if !stdout.end_with?("\n")
113
+ end
114
+ if stderr && !stderr.empty?
115
+ message << "--- ---\n"
116
+ message << "--- Chef Client Error Output ---\n"
117
+ message << "--- ---\n"
118
+ message << stderr
119
+ message << "\n" if !stderr.end_with?("\n")
120
+ end
121
+ if logs && !logs.empty?
122
+ message << "--- ---\n"
123
+ message << "--- Chef Client Logs ---\n"
124
+ message << "--- ---\n"
125
+ message << logs
126
+ end
127
+ message
128
+ end
129
+
130
+ class EventSink
131
+ def initialize
132
+ @events = []
133
+ end
134
+
135
+ attr_reader :events
136
+
137
+ def method_missing(method, *args)
138
+ @events << [ method, *args ]
139
+ end
140
+ end
141
+ end
142
+ end
@@ -24,7 +24,7 @@ module Cheffish
24
24
  end
25
25
 
26
26
  key_format[:type] = type_of(key)
27
- key_format[:size] = size_of(key)
27
+ key_format[:size] = size_of(key) if size_of(key)
28
28
  key_format[:pass_phrase] = pass_phrase if pass_phrase
29
29
  # TODO cipher, exponent
30
30
 
@@ -102,8 +102,12 @@ module Cheffish
102
102
  end
103
103
 
104
104
  def self.size_of(key)
105
- # TODO DSA -- this is RSA only
106
- key.n.num_bytes * 8
105
+ case key.class
106
+ when OpenSSL::PKey::RSA
107
+ key.n.num_bytes * 8
108
+ else
109
+ nil
110
+ end
107
111
  end
108
112
  end
109
113
  end
@@ -3,70 +3,24 @@ require 'chef/server_api'
3
3
  require 'cheffish/rspec/repository_support'
4
4
  require 'uri'
5
5
  require 'cheffish/basic_chef_client'
6
+ require 'cheffish/rspec/chef_run_wrapper'
7
+ require 'cheffish/rspec/recipe_run_wrapper'
6
8
 
7
9
  module Cheffish
8
10
  module RSpec
9
11
  module ChefRunSupport
10
12
  include ChefZero::RSpec
11
-
12
- def when_the_chef_12_server(*args, &block)
13
- if Gem::Version.new(ChefZero::VERSION) >= Gem::Version.new('3.1')
14
- when_the_chef_server(*args, :osc_compat => false, :single_org => false, &block)
15
- end
16
- end
13
+ include RepositorySupport
17
14
 
18
15
  def self.extended(klass)
19
16
  klass.class_eval do
20
- extend RepositorySupport
21
-
22
- def rest
23
- ::Chef::ServerAPI.new
24
- end
25
-
26
- def get(path, *args)
27
- if path[0] == '/'
28
- path = URI.join(rest.url, path)
29
- end
30
- rest.get(path, *args)
31
- end
32
-
33
- def chef_run
34
- converge if !@converged
35
- event_sink.events
36
- end
37
-
38
- def event_sink
39
- @event_sink ||= EventSink.new
40
- end
41
-
42
- def basic_chef_client
43
- @basic_chef_client ||= begin
44
- ::Cheffish::BasicChefClient.new(nil, event_sink)
45
- end
46
- end
47
-
48
- def load_recipe(&block)
49
- basic_chef_client.load_block(&block)
50
- end
51
-
52
- def run_recipe(&block)
53
- load_recipe(&block)
54
- converge
55
- end
56
-
57
- def reset_chef_client
58
- @event_sink = nil
59
- @basic_chef_client = nil
60
- @converged = false
61
- end
17
+ include ChefRunSupportInstanceMethods
18
+ end
19
+ end
62
20
 
63
- def converge
64
- if @converged
65
- raise "Already converged! Cannot converge twice, that's bad mojo."
66
- end
67
- @converged = true
68
- basic_chef_client.converge
69
- end
21
+ def when_the_chef_12_server(*args, **options, &block)
22
+ if Gem::Version.new(ChefZero::VERSION) >= Gem::Version.new('3.1')
23
+ when_the_chef_server(*args, :osc_compat => false, :single_org => false, **options, &block)
70
24
  end
71
25
  end
72
26
 
@@ -76,7 +30,7 @@ module Cheffish
76
30
  end
77
31
 
78
32
  after :each do
79
- if !@converged
33
+ if !chef_client.converge_failed? && !chef_client.converged?
80
34
  raise "Never tried to converge!"
81
35
  end
82
36
  end
@@ -89,18 +43,68 @@ module Cheffish
89
43
  end
90
44
  end
91
45
 
92
- class EventSink
93
- def initialize
94
- @events = []
46
+ module ChefRunSupportInstanceMethods
47
+ def rest
48
+ ::Chef::ServerAPI.new
95
49
  end
96
50
 
97
- attr_reader :events
51
+ def get(path, *args)
52
+ if path[0] == '/'
53
+ path = URI.join(rest.url, path)
54
+ end
55
+ rest.get(path, *args)
56
+ end
98
57
 
99
- def method_missing(method, *args)
100
- @events << [ method, *args ]
58
+ def chef_config
59
+ {}
60
+ end
61
+
62
+ def expect_recipe(&recipe)
63
+ expect(recipe(&recipe))
64
+ end
65
+
66
+ def recipe(&recipe)
67
+ RecipeRunWrapper.new(chef_config, &recipe)
68
+ end
69
+
70
+ def chef_client
71
+ @chef_client ||= ChefRunWrapper.new(chef_config)
72
+ end
73
+
74
+ def chef_run
75
+ converge if !chef_client.converged?
76
+ event_sink.events
77
+ end
78
+
79
+ def event_sink
80
+ chef_client.event_sink
101
81
  end
102
- end
103
82
 
83
+ def basic_chef_client
84
+ chef_client.client
85
+ end
86
+
87
+ def load_recipe(&recipe)
88
+ chef_client.client.load_block(&recipe)
89
+ end
90
+
91
+ def run_recipe(&recipe)
92
+ load_recipe(&recipe)
93
+ converge
94
+ end
95
+
96
+ def reset_chef_client
97
+ @event_sink = nil
98
+ @basic_chef_client = nil
99
+ end
100
+
101
+ def converge
102
+ if chef_client.converged?
103
+ raise "Already converged! Cannot converge twice, that's bad mojo."
104
+ end
105
+ chef_client.converge
106
+ end
107
+ end
104
108
  end
105
109
  end
106
110
  end
@@ -0,0 +1,5 @@
1
+ require 'cheffish/chef_run'
2
+
3
+ module Cheffish::RSpec
4
+ ChefRunWrapper = Cheffish::ChefRun
5
+ end
@@ -25,6 +25,20 @@ RSpec::Matchers.define :have_updated do |resource_name, *expected_actions|
25
25
  end
26
26
  end
27
27
 
28
+ RSpec::Matchers.define :be_idempotent do
29
+ match do |recipe|
30
+ @recipe = recipe
31
+ recipe.reset
32
+ recipe.converge
33
+ recipe.up_to_date?
34
+ end
35
+
36
+ failure_message {
37
+ "#{@recipe} is not idempotent! Converging it a second time caused updates.\n#{@recipe.output_for_failure_message}"
38
+ }
39
+ end
40
+
41
+
28
42
  RSpec::Matchers.define :update_acls do |acl_paths, expected_acls|
29
43
 
30
44
  errors = []
@@ -0,0 +1,22 @@
1
+ require 'cheffish/rspec/chef_run_wrapper'
2
+
3
+ module Cheffish
4
+ module RSpec
5
+ class RecipeRunWrapper < ChefRunWrapper
6
+ def initialize(chef_config, &recipe)
7
+ super(chef_config)
8
+ @recipe = recipe
9
+ end
10
+
11
+ attr_reader :recipe
12
+
13
+ def client
14
+ if !@client
15
+ super
16
+ @client.load_block(&recipe)
17
+ end
18
+ @client
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,3 @@
1
1
  module Cheffish
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cheffish
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Keiser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-02 00:00:00.000000000 Z
11
+ date: 2015-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef-zero
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '4.0'
19
+ version: '4.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '4.0'
26
+ version: '4.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: chef
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -111,13 +111,16 @@ files:
111
111
  - lib/cheffish/actor_provider_base.rb
112
112
  - lib/cheffish/basic_chef_client.rb
113
113
  - lib/cheffish/chef_provider_base.rb
114
+ - lib/cheffish/chef_run.rb
114
115
  - lib/cheffish/chef_run_data.rb
115
116
  - lib/cheffish/chef_run_listener.rb
116
117
  - lib/cheffish/key_formatter.rb
117
118
  - lib/cheffish/merged_config.rb
118
119
  - lib/cheffish/recipe_dsl.rb
119
120
  - lib/cheffish/rspec/chef_run_support.rb
121
+ - lib/cheffish/rspec/chef_run_wrapper.rb
120
122
  - lib/cheffish/rspec/matchers.rb
123
+ - lib/cheffish/rspec/recipe_run_wrapper.rb
121
124
  - lib/cheffish/rspec/repository_support.rb
122
125
  - lib/cheffish/server_api.rb
123
126
  - lib/cheffish/version.rb
@@ -156,9 +159,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
156
159
  version: '0'
157
160
  requirements: []
158
161
  rubyforge_project:
159
- rubygems_version: 2.4.5
162
+ rubygems_version: 2.4.2
160
163
  signing_key:
161
164
  specification_version: 4
162
165
  summary: A library to manipulate Chef in Chef.
163
166
  test_files: []
164
- has_rdoc: