cheffish 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: