knife-solo 0.3.0.pre3 → 0.3.0.pre4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +29 -4
  3. data/lib/chef/knife/solo_bootstrap.rb +1 -1
  4. data/lib/chef/knife/solo_clean.rb +1 -0
  5. data/lib/chef/knife/solo_cook.rb +32 -37
  6. data/lib/chef/knife/solo_init.rb +22 -16
  7. data/lib/knife-solo.rb +1 -0
  8. data/lib/knife-solo/berkshelf.rb +37 -0
  9. data/lib/knife-solo/bootstraps/linux.rb +12 -17
  10. data/lib/knife-solo/cookbook_manager.rb +120 -0
  11. data/lib/knife-solo/cookbook_manager_selector.rb +75 -0
  12. data/lib/knife-solo/deprecated_command.rb +5 -0
  13. data/lib/knife-solo/info.rb +1 -1
  14. data/lib/knife-solo/librarian.rb +39 -0
  15. data/lib/knife-solo/node_config_command.rb +10 -1
  16. data/lib/knife-solo/resources/knife.rb +3 -0
  17. data/lib/knife-solo/resources/solo.rb.erb +1 -0
  18. data/lib/knife-solo/ssh_command.rb +14 -2
  19. data/test/deprecated_command_test.rb +14 -1
  20. data/test/gemfiles/Gemfile.chef-10 +5 -0
  21. data/test/gemfiles/Gemfile.chef-11 +5 -0
  22. data/test/integration/cases/apache2_bootstrap.rb +8 -1
  23. data/test/integration/cases/cache_path_usage.rb +1 -1
  24. data/test/integration/cases/encrypted_data_bag.rb +1 -1
  25. data/test/integration/{centos5_6_test.rb → centos5_8_test.rb} +2 -2
  26. data/test/integration/centos6_3_test.rb +24 -0
  27. data/test/integration/debian7_knife_bootstrap_test.rb +1 -1
  28. data/test/solo_clean_test.rb +6 -0
  29. data/test/solo_cook_test.rb +60 -9
  30. data/test/solo_init_test.rb +149 -9
  31. data/test/ssh_command_test.rb +27 -0
  32. data/test/support/ec2_runner.rb +2 -2
  33. data/test/support/integration_test.rb +3 -3
  34. data/test/support/kitchen_helper.rb +1 -1
  35. data/test/support/secret_cookbook/metadata.rb +1 -0
  36. metadata +66 -63
@@ -0,0 +1,75 @@
1
+ require 'knife-solo/berkshelf'
2
+ require 'knife-solo/librarian'
3
+
4
+ module KnifeSolo
5
+ class CookbookManagerSelector
6
+ attr_reader :config, :ui
7
+
8
+ def initialize(config, ui)
9
+ @config = config
10
+ @ui = ui
11
+ end
12
+
13
+ def select(base)
14
+ Chef::Log.debug "Selecting cookbook manager..."
15
+
16
+ if (selected = select_or_disable_by_chef_config!)
17
+ return selected
18
+ elsif managers.empty?
19
+ Chef::Log.debug "All disabled by configuration"
20
+ return nil
21
+ end
22
+
23
+ selected = select_by_existing_conf_file(base) || select_by_installed_gem
24
+ if selected.nil?
25
+ Chef::Log.debug "Nothing selected"
26
+ # TODO: ui.msg "Recommended to use a cookbook manager"
27
+ end
28
+ selected
29
+ end
30
+
31
+ private
32
+
33
+ def managers
34
+ @managers ||= [
35
+ KnifeSolo::Berkshelf.new(config, ui),
36
+ KnifeSolo::Librarian.new(config, ui)
37
+ ]
38
+ end
39
+
40
+ def select_or_disable_by_chef_config!
41
+ managers.select! do |manager|
42
+ if (conf = manager.enabled_by_chef_config?)
43
+ Chef::Log.debug "#{manager} selected by configuration"
44
+ return manager
45
+ elsif conf == false
46
+ Chef::Log.debug "#{manager} disabled by configuration"
47
+ false
48
+ else # conf == nil
49
+ true
50
+ end
51
+ end
52
+ nil
53
+ end
54
+
55
+ def select_by_existing_conf_file(base)
56
+ managers.each do |manager|
57
+ if manager.conf_file_exists?(@base)
58
+ Chef::Log.debug "#{manager} selected because of existing #{manager.conf_file}"
59
+ return manager
60
+ end
61
+ end
62
+ nil
63
+ end
64
+
65
+ def select_by_installed_gem
66
+ managers.each do |manager|
67
+ if manager.gem_installed?
68
+ Chef::Log.debug "#{manager} selected because of installed gem"
69
+ return manager
70
+ end
71
+ end
72
+ nil
73
+ end
74
+ end
75
+ end
@@ -8,6 +8,11 @@ module KnifeSolo
8
8
  end
9
9
 
10
10
  banner deprecated
11
+ self.options = superclass.options
12
+
13
+ def self.load_deps
14
+ superclass.load_deps
15
+ end
11
16
  end
12
17
  end
13
18
 
@@ -1,6 +1,6 @@
1
1
  module KnifeSolo
2
2
  def self.version
3
- '0.3.0.pre3'
3
+ '0.3.0.pre4'
4
4
  end
5
5
 
6
6
  def self.post_install_message
@@ -0,0 +1,39 @@
1
+ require 'knife-solo/cookbook_manager'
2
+
3
+ module KnifeSolo
4
+ class Librarian
5
+ include CookbookManager
6
+
7
+ def self.gem_libraries
8
+ %w[librarian/action librarian/chef]
9
+ end
10
+
11
+ def self.conf_file_name
12
+ 'Cheffile'
13
+ end
14
+
15
+ def self.gem_name
16
+ 'librarian-chef'
17
+ end
18
+
19
+ def install!
20
+ ui.msg "Installing Librarian cookbooks..."
21
+ ::Librarian::Action::Resolve.new(env).run
22
+ ::Librarian::Action::Install.new(env).run
23
+ env.install_path
24
+ end
25
+
26
+ def env
27
+ @env ||= ::Librarian::Chef::Environment.new
28
+ end
29
+
30
+ def initial_config
31
+ "site 'http://community.opscode.com/api/v1'"
32
+ end
33
+
34
+ # Returns an array of strings to gitignore when bootstrapping
35
+ def gitignores
36
+ %w[/tmp/librarian/]
37
+ end
38
+ end
39
+ end
@@ -32,9 +32,18 @@ module KnifeSolo
32
32
  end
33
33
  end
34
34
 
35
+ def nodes_path
36
+ path = Chef::Config[:node_path]
37
+ path && File.exist?(path) ? path : 'nodes'
38
+ end
39
+
35
40
  def node_config
41
+ Pathname.new(@name_args[1] || "#{nodes_path}/#{node_name}.json")
42
+ end
43
+
44
+ def node_name
36
45
  # host method must be defined by the including class
37
- Pathname.new(@name_args[1] || "nodes/#{config[:chef_node_name] || host}.json")
46
+ config[:chef_node_name] || host
38
47
  end
39
48
 
40
49
  def generate_node_config
@@ -1,4 +1,7 @@
1
1
  cookbook_path ["cookbooks", "site-cookbooks"]
2
+ node_path "nodes"
2
3
  role_path "roles"
3
4
  data_bag_path "data_bags"
4
5
  #encrypted_data_bag_secret "data_bag_key"
6
+
7
+ knife[:berkshelf_path] = "cookbooks"
@@ -1,5 +1,6 @@
1
1
  base = File.expand_path('..', __FILE__)
2
2
 
3
+ nodes_path File.join(base, 'nodes')
3
4
  role_path File.join(base, 'roles')
4
5
  data_bag_path File.join(base, 'data_bags')
5
6
  encrypted_data_bag_secret File.join(base, 'data_bag_key')
@@ -154,7 +154,8 @@ module KnifeSolo
154
154
  class ExecResult
155
155
  attr_accessor :stdout, :stderr, :exit_code
156
156
 
157
- def initialize
157
+ def initialize(exit_code = nil)
158
+ @exit_code = exit_code
158
159
  @stdout = ""
159
160
  @stderr = ""
160
161
  end
@@ -208,7 +209,7 @@ module KnifeSolo
208
209
  command
209
210
  end
210
211
 
211
- def run_command(command, options={})
212
+ def run_command(command, options = {})
212
213
  defaults = {:process_sudo => true}
213
214
  options = defaults.merge(options)
214
215
 
@@ -254,6 +255,17 @@ module KnifeSolo
254
255
  result
255
256
  end
256
257
 
258
+ # Runs commands from the specified array until successful.
259
+ # Returns the result of the successful command or an ExecResult with
260
+ # exit_code 1 if all fail.
261
+ def run_with_fallbacks(commands, options = {})
262
+ commands.each do |command|
263
+ result = run_command(command, options)
264
+ return result if result.success?
265
+ end
266
+ ExecResult.new(1)
267
+ end
268
+
257
269
  # TODO:
258
270
  # - move this to a dedicated "portability" module?
259
271
  # - use ruby in all cases instead?
@@ -6,6 +6,10 @@ require 'knife-solo/deprecated_command'
6
6
  class DummyNewCommand < Chef::Knife
7
7
  banner "knife dummy_new_command"
8
8
 
9
+ option :foo,
10
+ :long => '--foo',
11
+ :description => 'Foo option'
12
+
9
13
  def run
10
14
  # calls #new_run so we can be sure this gets called
11
15
  new_run
@@ -32,13 +36,22 @@ class DeprecatedCommandTest < TestCase
32
36
  cmd.run
33
37
  end
34
38
 
35
- def test_runs_original_command
39
+ def test_runs_new_command
36
40
  cmd = command
37
41
  cmd.ui.stubs(:err)
38
42
  cmd.expects(:new_run)
39
43
  cmd.run
40
44
  end
41
45
 
46
+ def test_includes_options_from_new_command
47
+ assert DummyDeprecatedCommand.options.include?(:foo)
48
+ end
49
+
50
+ def test_loads_dependencies_from_new_command
51
+ DummyNewCommand.expects(:load_deps)
52
+ DummyDeprecatedCommand.load_deps
53
+ end
54
+
42
55
  def command(*args)
43
56
  DummyDeprecatedCommand.load_deps
44
57
  DummyDeprecatedCommand.new(args)
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '../..'
4
+
5
+ gem 'chef', '~> 10'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '../..'
4
+
5
+ gem 'chef', '~> 11'
@@ -6,8 +6,15 @@ require $base_dir.join('integration', 'cases', 'apache2_cook')
6
6
  module Apache2Bootstrap
7
7
  include Apache2Cook
8
8
 
9
+ def write_berksfile
10
+ File.open('Berksfile', 'w') do |f|
11
+ f.puts "site :opscode"
12
+ f.puts "cookbook 'apache2'"
13
+ end
14
+ end
15
+
9
16
  def test_apache2
10
- write_cheffile
17
+ write_berksfile
11
18
  assert_subcommand "bootstrap --run-list=recipe[apache2]"
12
19
  assert_match default_apache_message, http_response
13
20
  end
@@ -1,7 +1,7 @@
1
1
  module CachePathUsage
2
2
  def setup
3
3
  super
4
- FileUtils.cp_r $base_dir.join('support', 'cache_using_cookbook'), 'cookbooks/cache_using_cookbook'
4
+ FileUtils.cp_r $base_dir.join('support', 'cache_using_cookbook'), 'site-cookbooks/cache_using_cookbook'
5
5
  end
6
6
 
7
7
  def test_changing_a_cached_directory_between_cooks
@@ -4,7 +4,7 @@ module EncryptedDataBag
4
4
  def setup
5
5
  super
6
6
  FileUtils.cp $base_dir.join('support', 'data_bag_key'), 'data_bag_key'
7
- FileUtils.cp_r $base_dir.join('support', 'secret_cookbook'), 'cookbooks/secret_cookbook'
7
+ FileUtils.cp_r $base_dir.join('support', 'secret_cookbook'), 'site-cookbooks/secret_cookbook'
8
8
  File.open('.chef/knife.rb', 'a') do |f|
9
9
  f.puts 'encrypted_data_bag_secret "data_bag_key"'
10
10
  end
@@ -1,12 +1,12 @@
1
1
  require 'integration_helper'
2
2
 
3
- class Centos5_6Test < IntegrationTest
3
+ class Centos5_8Test < IntegrationTest
4
4
  def user
5
5
  "root"
6
6
  end
7
7
 
8
8
  def image_id
9
- "ami-3fe42456"
9
+ "ami-100e8a79"
10
10
  end
11
11
 
12
12
  include EmptyCook
@@ -0,0 +1,24 @@
1
+ require 'integration_helper'
2
+
3
+ class Centos6_3Test < IntegrationTest
4
+ def user
5
+ "root"
6
+ end
7
+
8
+ def image_id
9
+ "ami-86e15bef"
10
+ end
11
+
12
+ def prepare_server
13
+ disable_firewall
14
+ super
15
+ end
16
+
17
+ def disable_firewall
18
+ system "ssh #{connection_string} service iptables stop >> #{log_file}"
19
+ end
20
+
21
+ include EmptyCook
22
+ include Apache2Cook
23
+ include EncryptedDataBag
24
+ end
@@ -6,7 +6,7 @@ class Debian7KnifeBootstrapTest < IntegrationTest
6
6
  end
7
7
 
8
8
  def image_id
9
- "ami-0c8b1d65"
9
+ "ami-1d620e74"
10
10
  end
11
11
 
12
12
  def prepare_server
@@ -6,6 +6,12 @@ require 'chef/knife/solo_clean'
6
6
  class SoloCleanTest < TestCase
7
7
  include ValidationHelper::ValidationTests
8
8
 
9
+ def test_removes_provision_path
10
+ cmd = command('somehost', '--provisioning-path=/foo/bar')
11
+ cmd.expects(:run_command).with('rm -rf /foo/bar').returns(SuccessfulResult.new)
12
+ cmd.run
13
+ end
14
+
9
15
  def command(*args)
10
16
  knife_command(Chef::Knife::SoloClean, *args)
11
17
  end
@@ -2,8 +2,12 @@ require 'test_helper'
2
2
  require 'support/kitchen_helper'
3
3
  require 'support/validation_helper'
4
4
 
5
+ require 'berkshelf'
5
6
  require 'chef/cookbook/chefignore'
6
7
  require 'chef/knife/solo_cook'
8
+ require 'fileutils'
9
+ require 'knife-solo/berkshelf'
10
+ require 'knife-solo/librarian'
7
11
  require 'librarian/action/install'
8
12
 
9
13
  class SuccessfulResult
@@ -73,6 +77,60 @@ class SoloCookTest < TestCase
73
77
  assert_equal "/some/other/path", cmd.cookbook_paths[2].to_s
74
78
  end
75
79
 
80
+ def test_does_not_run_berkshelf_if_no_berkfile
81
+ in_kitchen do
82
+ Berkshelf::Berksfile.any_instance.expects(:install).never
83
+ command("somehost").run
84
+ end
85
+ end
86
+
87
+ def test_runs_berkshelf_if_berkfile_found
88
+ in_kitchen do
89
+ FileUtils.touch "Berksfile"
90
+ Berkshelf::Berksfile.any_instance.expects(:install)
91
+ command("somehost").run
92
+ end
93
+ end
94
+
95
+ def test_does_not_run_berkshelf_if_denied_by_option
96
+ in_kitchen do
97
+ FileUtils.touch "Berksfile"
98
+ Berkshelf::Berksfile.any_instance.expects(:install).never
99
+ command("somehost", "--no-berkshelf").run
100
+ end
101
+ end
102
+
103
+ def test_complains_if_berkshelf_gem_missing
104
+ in_kitchen do
105
+ FileUtils.touch "Berksfile"
106
+ cmd = command("somehost")
107
+ cmd.ui.expects(:err).with(regexp_matches(/berkshelf gem/))
108
+ KnifeSolo::Berkshelf.expects(:load_gem).returns(false)
109
+ Berkshelf::Berksfile.any_instance.expects(:install).never
110
+ cmd.run
111
+ end
112
+ end
113
+
114
+ def test_wont_complain_if_berkshelf_gem_missing_but_no_berkfile
115
+ in_kitchen do
116
+ cmd = command("somehost")
117
+ cmd.ui.expects(:err).never
118
+ KnifeSolo::Berkshelf.expects(:load_gem).never
119
+ Berkshelf::Berksfile.any_instance.expects(:install).never
120
+ cmd.run
121
+ end
122
+ end
123
+
124
+ def test_adds_berkshelf_path_to_cookbooks
125
+ in_kitchen do
126
+ FileUtils.touch "Berksfile"
127
+ KnifeSolo::Berkshelf.any_instance.stubs(:berkshelf_path).returns("berkshelf/path")
128
+ cmd = command("somehost")
129
+ cmd.run
130
+ assert_equal File.join(Dir.pwd, "berkshelf/path"), cmd.cookbook_paths[0].to_s
131
+ end
132
+ end
133
+
76
134
  def test_does_not_run_librarian_if_no_cheffile
77
135
  in_kitchen do
78
136
  Librarian::Action::Install.any_instance.expects(:run).never
@@ -100,8 +158,8 @@ class SoloCookTest < TestCase
100
158
  in_kitchen do
101
159
  FileUtils.touch "Cheffile"
102
160
  cmd = command("somehost")
103
- cmd.expects(:load_librarian).returns(false)
104
161
  cmd.ui.expects(:err).with(regexp_matches(/librarian-chef gem/))
162
+ KnifeSolo::Librarian.expects(:load_gem).returns(false)
105
163
  Librarian::Action::Install.any_instance.expects(:run).never
106
164
  cmd.run
107
165
  end
@@ -110,8 +168,8 @@ class SoloCookTest < TestCase
110
168
  def test_wont_complain_if_librarian_gem_missing_but_no_cheffile
111
169
  in_kitchen do
112
170
  cmd = command("somehost")
113
- cmd.expects(:load_librarian).never
114
171
  cmd.ui.expects(:err).never
172
+ KnifeSolo::Librarian.expects(:load_gem).never
115
173
  Librarian::Action::Install.any_instance.expects(:run).never
116
174
  cmd.run
117
175
  end
@@ -163,13 +221,6 @@ class SoloCookTest < TestCase
163
221
  end
164
222
  end
165
223
 
166
- def test_parses_chef_version_output
167
- version_string = "\r\nChef: 11.2.0\r\n"
168
- cmd = command("somehost")
169
- cmd.stubs(:run_command).returns(OpenStruct.new(:stdout => version_string))
170
- assert_equal '11.2.0', cmd.chef_version
171
- end
172
-
173
224
  def test_barks_if_chef_too_old
174
225
  in_kitchen do
175
226
  cmd = command("somehost")