knife-solo 0.0.15 → 0.1.0.pre1

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.
Files changed (62) hide show
  1. data/CHANGELOG.md +216 -0
  2. data/LICENSE +7 -0
  3. data/README.rdoc +127 -0
  4. data/Rakefile +31 -0
  5. data/lib/chef/knife/cook.rb +4 -131
  6. data/lib/chef/knife/kitchen.rb +4 -49
  7. data/lib/chef/knife/prepare.rb +4 -50
  8. data/lib/chef/knife/solo_bootstrap.rb +51 -0
  9. data/lib/chef/knife/solo_clean.rb +25 -0
  10. data/lib/chef/knife/solo_cook.rb +137 -0
  11. data/lib/chef/knife/solo_init.rb +59 -0
  12. data/lib/chef/knife/solo_prepare.rb +56 -0
  13. data/lib/chef/knife/wash_up.rb +4 -15
  14. data/lib/knife-solo/bootstraps.rb +1 -1
  15. data/lib/knife-solo/bootstraps/linux.rb +6 -4
  16. data/lib/knife-solo/bootstraps/sun_os.rb +9 -0
  17. data/lib/knife-solo/deprecated_command.rb +19 -0
  18. data/lib/knife-solo/info.rb +1 -1
  19. data/lib/knife-solo/kitchen_command.rb +5 -10
  20. data/lib/knife-solo/node_config_command.rb +17 -3
  21. data/lib/knife-solo/ssh_command.rb +3 -3
  22. data/test/bootstraps_test.rb +90 -0
  23. data/test/deprecated_command_test.rb +46 -0
  24. data/test/integration/cases/apache2_bootstrap.rb +15 -0
  25. data/test/integration/cases/apache2_cook.rb +28 -0
  26. data/test/integration/cases/empty_cook.rb +8 -0
  27. data/test/integration/cases/encrypted_data_bag.rb +27 -0
  28. data/test/integration/centos5_6_test.rb +19 -0
  29. data/test/integration/gentoo2011_test.rb +18 -0
  30. data/test/integration/omnios_r151004_test.rb +14 -0
  31. data/test/integration/scientific_linux_63_test.rb +15 -0
  32. data/test/integration/sles_11_test.rb +14 -0
  33. data/test/integration/ubuntu10_04_test.rb +15 -0
  34. data/test/integration/ubuntu12_04_bootstrap_test.rb +17 -0
  35. data/test/integration/ubuntu12_04_test.rb +15 -0
  36. data/test/integration_helper.rb +16 -0
  37. data/test/kitchen_command_test.rb +31 -0
  38. data/test/minitest/parallel.rb +41 -0
  39. data/test/node_config_command_test.rb +101 -0
  40. data/test/solo_bootstrap_test.rb +32 -0
  41. data/test/solo_clean_test.rb +12 -0
  42. data/test/solo_cook_test.rb +67 -0
  43. data/test/solo_init_test.rb +40 -0
  44. data/test/solo_prepare_test.rb +50 -0
  45. data/test/ssh_command_test.rb +100 -0
  46. data/test/support/config.yml.example +4 -0
  47. data/test/support/data_bag_key +1 -0
  48. data/test/support/ec2_runner.rb +122 -0
  49. data/test/support/integration_test.rb +94 -0
  50. data/test/support/issue_files/gentoo2011 +3 -0
  51. data/test/support/issue_files/sles11-sp1 +4 -0
  52. data/test/support/issue_files/ubuntu +2 -0
  53. data/test/support/kitchen_helper.rb +22 -0
  54. data/test/support/loggable.rb +18 -0
  55. data/test/support/secret_cookbook/metadata.rb +5 -0
  56. data/test/support/secret_cookbook/recipes/default.rb +8 -0
  57. data/test/support/ssh_config +3 -0
  58. data/test/support/test_case.rb +24 -0
  59. data/test/support/validation_helper.rb +39 -0
  60. data/test/test_helper.rb +7 -0
  61. metadata +99 -11
  62. data/lib/knife-solo/knife_solo_error.rb +0 -3
@@ -1,21 +1,10 @@
1
- require 'chef/knife'
2
- require 'knife-solo/ssh_command'
3
- require 'knife-solo/kitchen_command'
1
+ require 'chef/knife/solo_clean'
2
+ require 'knife-solo/deprecated_command'
4
3
 
5
4
  class Chef
6
5
  class Knife
7
- class WashUp < Knife
8
- include KnifeSolo::SshCommand
9
- include KnifeSolo::KitchenCommand
10
-
11
- banner "knife wash_up [user@]hostname"
12
-
13
- def run
14
- validate_first_cli_arg_is_a_hostname!
15
- super
16
- Chef::Config.from_file('solo.rb')
17
- run_command "rm -rf #{Chef::Config.file_cache_path}"
18
- end
6
+ class WashUp < SoloClean
7
+ include KnifeSolo::DeprecatedCommand
19
8
  end
20
9
  end
21
10
  end
@@ -112,7 +112,7 @@ module KnifeSolo
112
112
  include KnifeSolo::Bootstraps::InstallCommands
113
113
 
114
114
  def initialize(prepare)
115
- # instance of Chef::Knife::Prepare
115
+ # instance of Chef::Knife::SoloPrepare
116
116
  @prepare = prepare
117
117
  end
118
118
 
@@ -96,14 +96,16 @@ module KnifeSolo::Bootstraps
96
96
  {:type => "omnibus", :version => "FC15"}
97
97
  when %r{Fedora release.*? 16}
98
98
  {:type => "omnibus", :version => "FC16"}
99
+ when %r{Fedora release.*? 17}
100
+ {:type => "omnibus", :version => "FC17"}
99
101
  when %r{Scientific Linux.*? 5}
100
102
  {:type => "omnibus", :version => "RHEL5"}
101
103
  when %r{Scientific Linux.*? 6}
102
104
  {:type => "yum_omnibus", :version => "RHEL6"}
103
- when %r{SUSE Linux Enterprise Server 11 SP1}
104
- {:type => "zypper_gem", :version => "SLES11"}
105
- when %r{openSUSE 11.4}
106
- {:type => "zypper_gem", :version => "openSUSE"}
105
+ when %r{SUSE Linux Enterprise Server 1[12]}
106
+ {:type => "omnibus", :version => "SLES11"}
107
+ when %r{openSUSE 1[12]}
108
+ {:type => "omnibus", :version => "openSUSE"}
107
109
  when %r{This is \\n\.\\O \(\\s \\m \\r\) \\t}
108
110
  {:type => "emerge_gem", :version => "Gentoo"}
109
111
  else
@@ -0,0 +1,9 @@
1
+ module KnifeSolo::Bootstraps
2
+ class SunOS < Base
3
+ def bootstrap!
4
+ stream_command "sudo pkg install network/rsync web/ca-bundle"
5
+ stream_command "sudo bash -c 'echo ca_certificate=/etc/cacert.pem >> /etc/wgetrc'"
6
+ omnibus_install
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module KnifeSolo
2
+ module DeprecatedCommand
3
+
4
+ def self.included(other)
5
+ other.class_eval do
6
+ def self.deprecated
7
+ "`knife #{common_name}` is deprecated! Please use:\n #{superclass.banner}"
8
+ end
9
+
10
+ banner deprecated
11
+ end
12
+ end
13
+
14
+ def run
15
+ ui.warn self.class.deprecated
16
+ super
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,5 @@
1
1
  module KnifeSolo
2
2
  def self.version
3
- '0.0.15'
3
+ '0.1.0.pre1'
4
4
  end
5
5
  end
@@ -1,19 +1,14 @@
1
- require 'knife-solo/knife_solo_error'
2
-
3
1
  module KnifeSolo
4
2
  module KitchenCommand
5
- class OutOfKitchenError < KnifeSoloError
6
- def message
7
- "This command must be run inside a Chef solo kitchen."
8
- end
9
- end
10
-
11
3
  def self.required_files
12
4
  %w(solo.rb)
13
5
  end
14
6
 
15
- def run
16
- raise OutOfKitchenError.new unless required_files_present?
7
+ def validate_kitchen!
8
+ unless required_files_present?
9
+ ui.fatal "This command must be run inside a Chef solo kitchen."
10
+ exit 1
11
+ end
17
12
  end
18
13
 
19
14
  def required_files_present?
@@ -15,6 +15,20 @@ module KnifeSolo
15
15
  :short => "-N NAME",
16
16
  :long => "--node-name NAME",
17
17
  :description => "The Chef node name for your new node"
18
+
19
+ option :run_list,
20
+ :short => "-r RUN_LIST",
21
+ :long => "--run-list RUN_LIST",
22
+ :description => "Comma separated list of roles/recipes to put to node config (if it does not exist)",
23
+ :proc => lambda { |o| o.split(/[\s,]+/) },
24
+ :default => []
25
+
26
+ option :first_boot_attributes,
27
+ :short => "-j JSON_ATTRIBS",
28
+ :long => "--json-attributes",
29
+ :description => "A JSON string to be added to node config (if it does not exist)",
30
+ :proc => lambda { |o| JSON.parse(o) },
31
+ :default => {}
18
32
  end
19
33
  end
20
34
 
@@ -29,9 +43,9 @@ module KnifeSolo
29
43
  else
30
44
  ui.msg "Generating node config '#{node_config}'..."
31
45
  File.open(node_config, 'w') do |f|
32
- f.print <<-JSON.gsub(/^\s+/, '')
33
- { "run_list": [] }
34
- JSON
46
+ attributes = config[:first_boot_attributes] || {}
47
+ run_list = { :run_list => config[:run_list] || [] }
48
+ f.print attributes.merge(run_list).to_json
35
49
  end
36
50
  end
37
51
  end
@@ -2,7 +2,6 @@ module KnifeSolo
2
2
  module SshCommand
3
3
 
4
4
  def self.load_deps
5
- require 'knife-solo/knife_solo_error'
6
5
  require 'net/ssh'
7
6
  end
8
7
 
@@ -55,8 +54,9 @@ module KnifeSolo
55
54
 
56
55
  def validate_first_cli_arg_is_a_hostname!
57
56
  unless first_cli_arg_is_a_hostname?
58
- ui.msg opt_parser.help
59
- raise KnifeSoloError.new "need to pass atleast a [user@]hostname as the first argument"
57
+ show_usage
58
+ ui.fatal "You must specify [<user>@]<hostname> as the first argument"
59
+ exit 1
60
60
  end
61
61
  end
62
62
 
@@ -0,0 +1,90 @@
1
+ require 'test_helper'
2
+
3
+ require 'knife-solo/bootstraps'
4
+
5
+ class KnifeSolo::Bootstraps::StubOS < KnifeSolo::Bootstraps::Base
6
+ end
7
+
8
+ class KnifeSolo::Bootstraps::StubOS2 < KnifeSolo::Bootstraps::Base
9
+ def gem_packages ; ['chef'] ; end
10
+ def distro ; {:type => 'gem', :version => 'Fanny Faker'} ; end
11
+ def gem_install
12
+ # dont' actually install anything
13
+ end
14
+ end
15
+
16
+ class BootstrapsTest < TestCase
17
+ def test_bootstrap_class_exists_for
18
+ assert_equal true, KnifeSolo::Bootstraps.class_exists_for?('Stub OS')
19
+
20
+ assert_equal false, KnifeSolo::Bootstraps.class_exists_for?('Mythical OS')
21
+ end
22
+
23
+ def test_distro_raises_if_not_implemented
24
+ bootstrap = bootstrap_instance()
25
+
26
+ assert_raises RuntimeError do
27
+ bootstrap.distro()
28
+ end
29
+ end
30
+
31
+ def test_gem_packages_raises_if_not_implemented
32
+ bootstrap = bootstrap_instance()
33
+
34
+ assert_raises RuntimeError do
35
+ bootstrap.gem_packages()
36
+ end
37
+ end
38
+
39
+ def test_bootstrap_calls_appropriate_install_method
40
+ bootstrap = KnifeSolo::Bootstraps::StubOS2.new(mock)
41
+ bootstrap.stubs(:distro).returns({:type => 'disco_gem'})
42
+
43
+ bootstrap.expects(:disco_gem_install)
44
+
45
+ bootstrap.bootstrap!
46
+ end
47
+
48
+ def test_bootstrap_calls_pre_bootstrap_checks
49
+ bootstrap = KnifeSolo::Bootstraps::StubOS2.new(mock)
50
+
51
+ bootstrap.expects(:run_pre_bootstrap_checks)
52
+
53
+ bootstrap.bootstrap!
54
+ end
55
+
56
+ def test_bootstrap_delegates_to_knife_prepare
57
+ prepare = mock('chef::knife::prepare')
58
+ bootstrap = KnifeSolo::Bootstraps::StubOS2.new(prepare)
59
+
60
+ assert prepare == bootstrap.prepare
61
+ end
62
+
63
+ def test_darwin_checks_for_xcode_install_and_barfs_if_missing
64
+ bootstrap = KnifeSolo::Bootstraps::Darwin.new(mock)
65
+ # don't want to actually run anything
66
+ bootstrap.stubs(:gem_install)
67
+
68
+ bootstrap.expects(:has_xcode_installed?).returns(false)
69
+
70
+ assert_raises RuntimeError do
71
+ bootstrap.bootstrap!
72
+ end
73
+ end
74
+
75
+ def test_omnibus_install_methdod
76
+ bootstrap = KnifeSolo::Bootstraps::StubOS2.new(mock)
77
+ bootstrap.stubs(:distro).returns({:type => "omnibus"})
78
+
79
+ bootstrap.expects(:omnibus_install)
80
+
81
+ bootstrap.bootstrap!
82
+ end
83
+
84
+ # ***
85
+
86
+ def bootstrap_instance
87
+ @prepare = mock('Knife::Chef::SoloPrepare')
88
+ KnifeSolo::Bootstraps::StubOS.new(@prepare)
89
+ end
90
+ end
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ require 'chef/knife'
4
+ require 'knife-solo/deprecated_command'
5
+
6
+ class DummyNewCommand < Chef::Knife
7
+ banner "knife dummy_new_command"
8
+
9
+ def run
10
+ # calls #new_run so we can be sure this gets called
11
+ new_run
12
+ end
13
+
14
+ def new_run
15
+ # dummy
16
+ end
17
+ end
18
+
19
+ class DummyDeprecatedCommand < DummyNewCommand
20
+ include KnifeSolo::DeprecatedCommand
21
+ end
22
+
23
+ class DeprecatedCommandTest < TestCase
24
+ def test_help_warns_about_deprecation
25
+ $stdout.expects(:puts).with(regexp_matches(/deprecated!/))
26
+ assert_exits { command("--help") }
27
+ end
28
+
29
+ def test_warns_about_deprecation
30
+ cmd = command
31
+ cmd.ui.expects(:err).with(regexp_matches(/deprecated!/))
32
+ cmd.run
33
+ end
34
+
35
+ def test_runs_original_command
36
+ cmd = command
37
+ cmd.ui.stubs(:err)
38
+ cmd.expects(:new_run)
39
+ cmd.run
40
+ end
41
+
42
+ def command(*args)
43
+ DummyDeprecatedCommand.load_deps
44
+ DummyDeprecatedCommand.new(args)
45
+ end
46
+ end
@@ -0,0 +1,15 @@
1
+ # Tries to bootstrap with apache2 cookbook and
2
+ # verifies the "It Works!" page is present.
3
+
4
+ require $base_dir.join('integration', 'cases', 'apache2_cook')
5
+
6
+ module Apache2Bootstrap
7
+ include Apache2Cook
8
+
9
+ def test_apache2
10
+ write_cheffile
11
+ system "librarian-chef install >> #{log_file}"
12
+ assert_subcommand "bootstrap --run-list=recipe[apache2]"
13
+ assert_match default_apache_message, http_response
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ # Tries to cook with apache2 cookbook and
2
+ # verifies the "It Works!" page is present.
3
+ module Apache2Cook
4
+ def write_cheffile
5
+ File.open('Cheffile', 'w') do |f|
6
+ f.print <<-CHEF
7
+ site 'http://community.opscode.com/api/v1'
8
+ cookbook 'apache2'
9
+ CHEF
10
+ end
11
+ end
12
+
13
+ def http_response
14
+ Net::HTTP.get(URI.parse("http://"+server.public_ip_address))
15
+ end
16
+
17
+ def default_apache_message
18
+ /It works!/
19
+ end
20
+
21
+ def test_apache2
22
+ write_cheffile
23
+ system "librarian-chef install >> #{log_file}"
24
+ write_nodefile(run_list: ["recipe[apache2]"])
25
+ assert_subcommand "cook"
26
+ assert_match default_apache_message, http_response
27
+ end
28
+ end
@@ -0,0 +1,8 @@
1
+ # Tries to run cook on the box
2
+ module EmptyCook
3
+ def test_empty_cook
4
+ write_nodefile(run_list: [])
5
+ assert_subcommand "cook"
6
+ end
7
+ end
8
+
@@ -0,0 +1,27 @@
1
+ require 'chef/encrypted_data_bag_item'
2
+
3
+ module EncryptedDataBag
4
+ def setup
5
+ super
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'
8
+ @password = "essential particles busy loud"
9
+ create_data_bag
10
+ end
11
+
12
+ def create_data_bag
13
+ secret = Chef::EncryptedDataBagItem.load_secret('data_bag_key')
14
+ data = {"id" => "passwords", "admin" => @password}
15
+ encrypted_data = Chef::EncryptedDataBagItem.encrypt_data_bag_item(data, secret)
16
+ write_json_file('data_bags/dev/passwords.json', encrypted_data)
17
+ end
18
+
19
+ # Test that we can read an encrypted data bag value
20
+ # NOTE: This shells out to ssh, so may not be windows-compatible
21
+ def test_reading_encrypted_data_bag
22
+ write_nodefile(run_list: ["recipe[secret_cookbook]"])
23
+ assert_subcommand "cook"
24
+ actual = `ssh #{connection_string} cat /etc/admin_password`
25
+ assert_equal @password, actual
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ require 'integration_helper'
2
+
3
+ class Centos5_6Test < IntegrationTest
4
+ def user
5
+ "root"
6
+ end
7
+
8
+ def image_id
9
+ "ami-3fe42456"
10
+ end
11
+
12
+ def default_apache_message
13
+ /Apache 2 Test Page/
14
+ end
15
+
16
+ include EmptyCook
17
+ include Apache2Cook
18
+ include EncryptedDataBag
19
+ end
@@ -0,0 +1,18 @@
1
+ require 'integration_helper'
2
+
3
+ class Gentoo2011Test < IntegrationTest
4
+ def user
5
+ "root"
6
+ end
7
+
8
+ def image_id
9
+ "ami-ae49b7c7"
10
+ end
11
+
12
+ # `emerge chef` takes a very long time (~ 50 minutes) on an m1.small
13
+ # Uncoment this if you need to verify Gentoo operation
14
+
15
+ # include EmptyCook
16
+ # include Apache2Cook
17
+ # include EncryptedDataBag
18
+ end