test-kitchen 1.2.1 → 1.3.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.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +1 -1
  3. data/.rubocop.yml +3 -0
  4. data/.travis.yml +20 -9
  5. data/CHANGELOG.md +219 -108
  6. data/Gemfile +10 -6
  7. data/Guardfile +38 -9
  8. data/README.md +11 -1
  9. data/Rakefile +21 -37
  10. data/bin/kitchen +4 -4
  11. data/features/kitchen_action_commands.feature +161 -0
  12. data/features/kitchen_console_command.feature +34 -0
  13. data/features/kitchen_diagnose_command.feature +64 -0
  14. data/features/kitchen_init_command.feature +29 -17
  15. data/features/kitchen_list_command.feature +2 -2
  16. data/features/kitchen_login_command.feature +56 -0
  17. data/features/{sink_command.feature → kitchen_sink_command.feature} +0 -0
  18. data/features/kitchen_test_command.feature +88 -0
  19. data/features/step_definitions/gem_steps.rb +8 -6
  20. data/features/step_definitions/git_steps.rb +4 -2
  21. data/features/step_definitions/output_steps.rb +5 -0
  22. data/features/support/env.rb +12 -9
  23. data/lib/kitchen.rb +60 -38
  24. data/lib/kitchen/base64_stream.rb +55 -0
  25. data/lib/kitchen/busser.rb +124 -58
  26. data/lib/kitchen/cli.rb +121 -38
  27. data/lib/kitchen/collection.rb +3 -3
  28. data/lib/kitchen/color.rb +4 -4
  29. data/lib/kitchen/command.rb +78 -11
  30. data/lib/kitchen/command/action.rb +3 -2
  31. data/lib/kitchen/command/console.rb +12 -5
  32. data/lib/kitchen/command/diagnose.rb +17 -3
  33. data/lib/kitchen/command/driver_discover.rb +26 -7
  34. data/lib/kitchen/command/exec.rb +41 -0
  35. data/lib/kitchen/command/list.rb +44 -14
  36. data/lib/kitchen/command/login.rb +2 -1
  37. data/lib/kitchen/command/sink.rb +2 -1
  38. data/lib/kitchen/command/test.rb +5 -4
  39. data/lib/kitchen/config.rb +146 -14
  40. data/lib/kitchen/configurable.rb +314 -0
  41. data/lib/kitchen/data_munger.rb +522 -18
  42. data/lib/kitchen/diagnostic.rb +43 -4
  43. data/lib/kitchen/driver.rb +4 -4
  44. data/lib/kitchen/driver/base.rb +80 -115
  45. data/lib/kitchen/driver/dummy.rb +34 -6
  46. data/lib/kitchen/driver/proxy.rb +14 -3
  47. data/lib/kitchen/driver/ssh_base.rb +61 -7
  48. data/lib/kitchen/errors.rb +109 -9
  49. data/lib/kitchen/generator/driver_create.rb +39 -5
  50. data/lib/kitchen/generator/init.rb +130 -45
  51. data/lib/kitchen/instance.rb +162 -28
  52. data/lib/kitchen/lazy_hash.rb +79 -7
  53. data/lib/kitchen/loader/yaml.rb +159 -27
  54. data/lib/kitchen/logger.rb +267 -21
  55. data/lib/kitchen/logging.rb +30 -3
  56. data/lib/kitchen/login_command.rb +11 -2
  57. data/lib/kitchen/metadata_chopper.rb +2 -2
  58. data/lib/kitchen/provisioner.rb +4 -4
  59. data/lib/kitchen/provisioner/base.rb +107 -103
  60. data/lib/kitchen/provisioner/chef/berkshelf.rb +36 -8
  61. data/lib/kitchen/provisioner/chef/librarian.rb +40 -11
  62. data/lib/kitchen/provisioner/chef_base.rb +206 -167
  63. data/lib/kitchen/provisioner/chef_solo.rb +25 -7
  64. data/lib/kitchen/provisioner/chef_zero.rb +105 -29
  65. data/lib/kitchen/provisioner/dummy.rb +1 -1
  66. data/lib/kitchen/provisioner/shell.rb +21 -6
  67. data/lib/kitchen/rake_tasks.rb +8 -3
  68. data/lib/kitchen/shell_out.rb +15 -18
  69. data/lib/kitchen/ssh.rb +122 -27
  70. data/lib/kitchen/state_file.rb +24 -7
  71. data/lib/kitchen/thor_tasks.rb +9 -4
  72. data/lib/kitchen/util.rb +43 -118
  73. data/lib/kitchen/version.rb +1 -1
  74. data/lib/vendor/hash_recursive_merge.rb +10 -2
  75. data/spec/kitchen/base64_stream_spec.rb +77 -0
  76. data/spec/kitchen/busser_spec.rb +490 -0
  77. data/spec/kitchen/collection_spec.rb +10 -10
  78. data/spec/kitchen/color_spec.rb +2 -2
  79. data/spec/kitchen/config_spec.rb +234 -62
  80. data/spec/kitchen/configurable_spec.rb +490 -0
  81. data/spec/kitchen/data_munger_spec.rb +1070 -862
  82. data/spec/kitchen/diagnostic_spec.rb +79 -0
  83. data/spec/kitchen/driver/base_spec.rb +80 -85
  84. data/spec/kitchen/driver/dummy_spec.rb +43 -14
  85. data/spec/kitchen/driver/proxy_spec.rb +134 -0
  86. data/spec/kitchen/driver/ssh_base_spec.rb +644 -0
  87. data/spec/kitchen/driver_spec.rb +15 -15
  88. data/spec/kitchen/errors_spec.rb +309 -0
  89. data/spec/kitchen/instance_spec.rb +143 -46
  90. data/spec/kitchen/lazy_hash_spec.rb +36 -9
  91. data/spec/kitchen/loader/yaml_spec.rb +237 -226
  92. data/spec/kitchen/logger_spec.rb +419 -0
  93. data/spec/kitchen/logging_spec.rb +59 -0
  94. data/spec/kitchen/login_command_spec.rb +49 -0
  95. data/spec/kitchen/metadata_chopper_spec.rb +82 -0
  96. data/spec/kitchen/platform_spec.rb +4 -4
  97. data/spec/kitchen/provisioner/base_spec.rb +65 -125
  98. data/spec/kitchen/provisioner/chef_base_spec.rb +798 -0
  99. data/spec/kitchen/provisioner/chef_solo_spec.rb +316 -0
  100. data/spec/kitchen/provisioner/chef_zero_spec.rb +624 -0
  101. data/spec/kitchen/provisioner/shell_spec.rb +269 -0
  102. data/spec/kitchen/provisioner_spec.rb +6 -6
  103. data/spec/kitchen/shell_out_spec.rb +143 -0
  104. data/spec/kitchen/ssh_spec.rb +683 -0
  105. data/spec/kitchen/state_file_spec.rb +28 -21
  106. data/spec/kitchen/suite_spec.rb +7 -7
  107. data/spec/kitchen/util_spec.rb +68 -10
  108. data/spec/kitchen_spec.rb +107 -0
  109. data/spec/spec_helper.rb +18 -13
  110. data/support/chef-client-zero.rb +10 -9
  111. data/support/chef_helpers.sh +16 -0
  112. data/support/download_helpers.sh +109 -0
  113. data/test-kitchen.gemspec +42 -33
  114. metadata +107 -33
@@ -0,0 +1,49 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2014, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require_relative "../spec_helper"
20
+
21
+ require "kitchen/login_command"
22
+
23
+ describe Kitchen::LoginCommand do
24
+
25
+ let(:argv) { Array.new }
26
+ let(:opts) { Hash.new }
27
+
28
+ let(:cmd) { Kitchen::LoginCommand.new(argv, opts) }
29
+
30
+ it "#cmd_array defaults to an empty array" do
31
+ Kitchen::LoginCommand.new(nil, opts).cmd_array.must_equal []
32
+ end
33
+
34
+ it "#cmd_array returns the command array from the constructor" do
35
+ argv.concat(["one", "-o", "two"])
36
+
37
+ cmd.cmd_array.must_equal ["one", "-o", "two"]
38
+ end
39
+
40
+ it "#options defaults to an empty hash" do
41
+ Kitchen::LoginCommand.new(argv, nil).options.must_equal {}
42
+ end
43
+
44
+ it "#options returns the options hash from the constructor" do
45
+ opts[:cake] = "yummy"
46
+
47
+ cmd.options.must_equal(:cake => "yummy")
48
+ end
49
+ end
@@ -0,0 +1,82 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2014, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require_relative "../spec_helper"
20
+
21
+ require "kitchen/metadata_chopper"
22
+
23
+ describe Kitchen::MetadataChopper do
24
+
25
+ before do
26
+ FakeFS.activate!
27
+ FileUtils.mkdir_p("/tmp")
28
+ end
29
+
30
+ after do
31
+ FakeFS.deactivate!
32
+ FakeFS::FileSystem.clear
33
+ end
34
+
35
+ let(:described_class) { Kitchen::MetadataChopper }
36
+
37
+ describe ".new" do
38
+
39
+ it "contains a :name attribute" do
40
+ stub_metadata!("banzai")
41
+
42
+ described_class.new("/tmp/metadata.rb")[:name].must_equal "banzai"
43
+ end
44
+
45
+ it "contains a :version attribute" do
46
+ stub_metadata!("foobar", "1.2.3")
47
+
48
+ described_class.new("/tmp/metadata.rb")[:version].must_equal "1.2.3"
49
+ end
50
+ end
51
+
52
+ describe ".extract" do
53
+
54
+ it "returns a tuple" do
55
+ stub_metadata!("foo", "1.2.3")
56
+
57
+ described_class.extract("/tmp/metadata.rb").must_equal ["foo", "1.2.3"]
58
+ end
59
+
60
+ it "returns nils for a name or version that isn't present" do
61
+ File.open("/tmp/metadata.rb", "wb") do |f|
62
+ f.write %{maintainer "Michael Bluth"\n}
63
+ end
64
+
65
+ described_class.extract("/tmp/metadata.rb").must_equal [nil, nil]
66
+ end
67
+ end
68
+
69
+ def stub_metadata!(name = "foobar", version = "5.2.1")
70
+ File.open("/tmp/metadata.rb", "wb") do |f|
71
+ f.write <<-METADATA_RB.gsub(/^ {8}/, "")
72
+ name "#{name}"
73
+ maintainer "Michael Bluth"
74
+ maintainer_email "michael@bluth.com"
75
+ license "Apache 2.0"
76
+ description "Doing stuff!"
77
+ long_description "Doing stuff!"
78
+ version "#{version}"
79
+ METADATA_RB
80
+ end
81
+ end
82
+ end
@@ -16,14 +16,14 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
- require_relative '../spec_helper'
19
+ require_relative "../spec_helper"
20
20
 
21
- require 'kitchen/errors'
22
- require 'kitchen/platform'
21
+ require "kitchen/errors"
22
+ require "kitchen/platform"
23
23
 
24
24
  describe Kitchen::Platform do
25
25
 
26
- let(:opts) do ; { :name => 'plata' } ; end
26
+ let(:opts) do; { :name => "plata" }; end
27
27
  let(:platform) { Kitchen::Platform.new(opts) }
28
28
 
29
29
  it "raises an ArgumentError if name is missing" do
@@ -16,79 +16,31 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
- require_relative '../../spec_helper'
20
- require 'stringio'
19
+ require_relative "../../spec_helper"
20
+ require "logger"
21
+ require "stringio"
21
22
 
22
- require 'kitchen'
23
-
24
- module Kitchen
25
-
26
- module Provisioner
27
-
28
- class StaticDefaults < Base
29
-
30
- default_config :rank, "captain"
31
- default_config :tunables, { "foo" => "fa" }
32
- default_config :nice, true
33
- end
34
-
35
- class SubclassDefaults < StaticDefaults
36
-
37
- default_config :yea, "ya"
38
- end
39
-
40
- class ComputedDefaults < Base
41
-
42
- default_config :beans, "kidney"
43
- default_config :fetch_command, "curl"
44
- default_config :beans_url do |provisioner|
45
- "http://gim.me/#{provisioner[:beans]}"
46
- end
47
- default_config :command do |provisioner|
48
- "#{provisioner[:fetch_command]} #{provisioner[:beans_url]}"
49
- end
50
- default_config :fetch_url do |provisioner|
51
- "http://gim.me/beans-for/#{provisioner.instance.name}"
52
- end
53
- end
54
- end
55
- end
23
+ require "kitchen"
56
24
 
57
25
  describe Kitchen::Provisioner::Base do
58
26
 
27
+ let(:logged_output) { StringIO.new }
28
+ let(:logger) { Logger.new(logged_output) }
59
29
  let(:config) { Hash.new }
60
- let(:logger_io) { StringIO.new }
61
- let(:instance_logger) { Kitchen::Logger.new(:logdev => logger_io) }
62
30
 
63
31
  let(:instance) do
64
- stub(:name => "coolbeans", :logger => instance_logger)
32
+ stub(:name => "coolbeans", :logger => logger)
65
33
  end
66
34
 
67
35
  let(:provisioner) do
68
- p = Kitchen::Provisioner::Base.new(config)
69
- p.instance = instance
70
- p
36
+ Kitchen::Provisioner::Base.new(config).finalize_config!(instance)
71
37
  end
72
38
 
73
- it "#instance returns its instance" do
74
- provisioner.instance.must_equal instance
75
- end
76
-
77
- it "#name returns its class name as a string" do
39
+ it "#name returns the name of the provisioner" do
78
40
  provisioner.name.must_equal "Base"
79
41
  end
80
42
 
81
- describe "user config" do
82
-
83
- before do
84
- config[:animals] = %w{cats dogs}
85
- config[:coolness] = true
86
- end
87
-
88
- it "injects config into the provisioner" do
89
- provisioner[:animals].must_equal ["cats", "dogs"]
90
- provisioner[:coolness].must_equal true
91
- end
43
+ describe "configuration" do
92
44
 
93
45
  it ":root_path defaults to /tmp/kitchen" do
94
46
  provisioner[:root_path].must_equal "/tmp/kitchen"
@@ -97,99 +49,87 @@ describe Kitchen::Provisioner::Base do
97
49
  it ":sudo defaults to true" do
98
50
  provisioner[:sudo].must_equal true
99
51
  end
100
-
101
- it "#config_keys returns the config keys" do
102
- provisioner.config_keys.sort.
103
- must_equal [:animals, :coolness, :root_path, :sudo]
104
- end
105
52
  end
106
53
 
107
- describe ".default_config" do
54
+ describe "#logger" do
108
55
 
109
- describe "static default config" do
56
+ before { @klog = Kitchen.logger }
57
+ after { Kitchen.logger = @klog }
110
58
 
111
- let(:provisioner) do
112
- p = Kitchen::Provisioner::StaticDefaults.new(config)
113
- p.instance = instance
114
- p
115
- end
59
+ it "returns the instance's logger" do
60
+ provisioner.send(:logger).must_equal logger
61
+ end
116
62
 
117
- it "uses default config" do
118
- provisioner[:rank].must_equal "captain"
119
- provisioner[:tunables]["foo"].must_equal "fa"
120
- provisioner[:nice].must_equal true
121
- end
63
+ it "returns the default logger if instance's logger is not set" do
64
+ provisioner = Kitchen::Provisioner::Base.new(config)
65
+ Kitchen.logger = "yep"
66
+
67
+ provisioner.send(:logger).must_equal Kitchen.logger
68
+ end
69
+ end
122
70
 
123
- it "uses user config over default config" do
124
- config[:rank] = "commander"
125
- config[:nice] = :maybe
71
+ [:init_command, :install_command, :prepare_command, :run_command].each do |cmd|
126
72
 
127
- provisioner[:rank].must_equal "commander"
128
- provisioner[:tunables]["foo"].must_equal "fa"
129
- provisioner[:nice].must_equal :maybe
130
- end
73
+ it "has a #{cmd} method" do
74
+ provisioner.public_send(cmd).must_be_nil
131
75
  end
76
+ end
132
77
 
133
- describe "inherited static default config" do
78
+ describe "sandbox" do
134
79
 
135
- let(:provisioner) do
136
- p = Kitchen::Provisioner::SubclassDefaults.new(config)
137
- p.instance = instance
138
- p
80
+ after do
81
+ begin
82
+ provisioner.cleanup_sandbox
83
+ rescue # rubocop:disable Lint/HandleExceptions
139
84
  end
85
+ end
140
86
 
141
- it "contains defaults from superclass" do
142
- provisioner[:rank].must_equal "captain"
143
- provisioner[:tunables]["foo"].must_equal "fa"
144
- provisioner[:nice].must_equal true
145
- provisioner[:yea].must_equal "ya"
146
- end
87
+ it "raises ClientError if #sandbox_path is called before #create_sandbox" do
88
+ proc { provisioner.sandbox_path }.must_raise Kitchen::ClientError
89
+ end
147
90
 
148
- it "uses user config over default config" do
149
- config[:rank] = "commander"
150
- config[:nice] = :maybe
91
+ it "#create_sandbox creates a temporary directory" do
92
+ provisioner.create_sandbox
151
93
 
152
- provisioner[:rank].must_equal "commander"
153
- provisioner[:tunables]["foo"].must_equal "fa"
154
- provisioner[:nice].must_equal :maybe
155
- provisioner[:yea].must_equal "ya"
156
- end
94
+ File.directory?(provisioner.sandbox_path).must_equal true
95
+ format("%o", File.stat(provisioner.sandbox_path).mode)[1, 4].
96
+ must_equal "0755"
157
97
  end
158
98
 
159
- describe "computed default config" do
99
+ it "#create_sandbox logs an info message" do
100
+ provisioner.create_sandbox
160
101
 
161
- let(:provisioner) do
162
- p = Kitchen::Provisioner::ComputedDefaults.new(config)
163
- p.instance = instance
164
- p
165
- end
102
+ logged_output.string.must_match info_line("Preparing files for transfer")
103
+ end
166
104
 
167
- it "uses computed config" do
168
- provisioner[:beans_url].must_equal "http://gim.me/kidney"
169
- provisioner[:command].must_equal "curl http://gim.me/kidney"
170
- end
105
+ it "#create_sandbox logs a debug message" do
106
+ provisioner.create_sandbox
171
107
 
172
- it "has access to instance object" do
173
- provisioner[:fetch_url].must_equal "http://gim.me/beans-for/coolbeans"
174
- end
108
+ logged_output.string.
109
+ must_match debug_line_starting_with("Creating local sandbox in ")
110
+ end
175
111
 
176
- it "uses user config over default config" do
177
- config[:command] = "echo listentome"
112
+ it "#cleanup_sandbox deletes the sandbox directory" do
113
+ provisioner.create_sandbox
114
+ provisioner.cleanup_sandbox
178
115
 
179
- provisioner[:command].must_equal "echo listentome"
180
- end
116
+ File.directory?(provisioner.sandbox_path).must_equal false
181
117
  end
182
- end
183
118
 
184
- describe "#logger" do
119
+ it "#cleanup_sandbox logs a debug message" do
120
+ provisioner.create_sandbox
121
+ provisioner.cleanup_sandbox
185
122
 
186
- it "if instance is set, use its logger" do
187
- provisioner.send(:logger).must_equal instance_logger
123
+ logged_output.string.
124
+ must_match debug_line_starting_with("Cleaning up local sandbox in ")
188
125
  end
189
126
 
190
- it "if instance is not set, use Kitchen.logger" do
191
- provisioner.instance = nil
192
- provisioner.send(:logger).must_equal Kitchen.logger
127
+ def info_line(msg)
128
+ %r{^I, .* : #{Regexp.escape(msg)}$}
129
+ end
130
+
131
+ def debug_line_starting_with(msg)
132
+ %r{^D, .* : #{Regexp.escape(msg)}}
193
133
  end
194
134
  end
195
135
 
@@ -0,0 +1,798 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2014, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require_relative "../../spec_helper"
20
+
21
+ require "kitchen"
22
+ require "kitchen/provisioner/chef_base"
23
+
24
+ describe Kitchen::Provisioner::ChefBase do
25
+
26
+ let(:logged_output) { StringIO.new }
27
+ let(:logger) { Logger.new(logged_output) }
28
+
29
+ let(:config) do
30
+ { :test_base_path => "/basist", :kitchen_root => "/rooty" }
31
+ end
32
+
33
+ let(:suite) do
34
+ stub(:name => "fries")
35
+ end
36
+
37
+ let(:instance) do
38
+ stub(:name => "coolbeans", :logger => logger, :suite => suite)
39
+ end
40
+
41
+ let(:provisioner) do
42
+ Class.new(Kitchen::Provisioner::ChefBase) {
43
+ def calculate_path(path, _opts = {})
44
+ "<calculated>/#{path}"
45
+ end
46
+ }.new(config).finalize_config!(instance)
47
+ end
48
+
49
+ describe "configuration" do
50
+
51
+ it ":require_chef_omnibus defaults to true" do
52
+ provisioner[:require_chef_omnibus].must_equal true
53
+ end
54
+
55
+ it ":chef_omnibus_url has a default" do
56
+ provisioner[:chef_omnibus_url].
57
+ must_equal "https://www.chef.io/chef/install.sh"
58
+ end
59
+
60
+ it ":chef_omnibus_root has a default" do
61
+ provisioner[:chef_omnibus_root].must_equal "/opt/chef"
62
+ end
63
+
64
+ it ":chef_omnibus_install_options defaults to nil" do
65
+ provisioner[:chef_omnibus_install_options].must_equal nil
66
+ end
67
+
68
+ it ":run_list defaults to an empty array" do
69
+ provisioner[:run_list].must_equal []
70
+ end
71
+
72
+ it ":attributes defaults to an empty hash" do
73
+ provisioner[:attributes].must_equal Hash.new
74
+ end
75
+
76
+ it ":log_file defaults to nil" do
77
+ provisioner[:log_file].must_equal nil
78
+ end
79
+
80
+ it ":cookbook_files_glob includes recipes" do
81
+ provisioner[:cookbook_files_glob].must_match %r{,recipes/}
82
+ end
83
+
84
+ it ":data_path uses calculate_path and is expanded" do
85
+ provisioner[:data_path].must_equal "/rooty/<calculated>/data"
86
+ end
87
+
88
+ it ":data_bags_path uses calculate_path and is expanded" do
89
+ provisioner[:data_bags_path].must_equal "/rooty/<calculated>/data_bags"
90
+ end
91
+
92
+ it ":environments_path uses calculate_path and is expanded" do
93
+ provisioner[:environments_path].
94
+ must_equal "/rooty/<calculated>/environments"
95
+ end
96
+
97
+ it ":nodes_path uses calculate_path and is expanded" do
98
+ provisioner[:nodes_path].must_equal "/rooty/<calculated>/nodes"
99
+ end
100
+
101
+ it ":roles_path uses calculate_path and is expanded" do
102
+ provisioner[:roles_path].must_equal "/rooty/<calculated>/roles"
103
+ end
104
+
105
+ it ":clients_path uses calculate_path and is expanded" do
106
+ provisioner[:clients_path].must_equal "/rooty/<calculated>/clients"
107
+ end
108
+
109
+ it "...secret_key_path uses calculate_path and is expanded" do
110
+ provisioner[:encrypted_data_bag_secret_key_path].
111
+ must_equal "/rooty/<calculated>/encrypted_data_bag_secret_key"
112
+ end
113
+ end
114
+
115
+ describe "#install_command" do
116
+
117
+ it "returns nil if :require_chef_omnibus is falsey" do
118
+ config[:require_chef_omnibus] = false
119
+
120
+ provisioner.install_command.must_equal nil
121
+ end
122
+
123
+ it "uses bourne shell (sh)" do
124
+ provisioner.install_command.must_match(/\Ash -c '$/)
125
+ end
126
+
127
+ it "ends with a single quote" do
128
+ provisioner.install_command.must_match(/'\Z/)
129
+ end
130
+
131
+ it "installs chef using :chef_omnibus_url, if necessary" do
132
+ config[:chef_omnibus_url] = "FROM_HERE"
133
+
134
+ provisioner.install_command.must_match regexify(
135
+ "do_download FROM_HERE /tmp/install.sh")
136
+ end
137
+
138
+ it "will install a specific version of chef, if necessary" do
139
+ config[:require_chef_omnibus] = "1.2.3"
140
+
141
+ provisioner.install_command.must_match regexify(
142
+ "sudo -E sh /tmp/install.sh -v 1.2.3")
143
+ provisioner.install_command.must_match regexify(
144
+ "Installing Chef Omnibus (1.2.3)", :partial_line)
145
+ end
146
+
147
+ it "will install a major/minor version of chef, if necessary" do
148
+ config[:require_chef_omnibus] = "11.10"
149
+
150
+ provisioner.install_command.must_match regexify(
151
+ "sudo -E sh /tmp/install.sh -v 11.10")
152
+ provisioner.install_command.must_match regexify(
153
+ "Installing Chef Omnibus (11.10)", :partial_line)
154
+ end
155
+
156
+ it "will install a major version of chef, if necessary" do
157
+ config[:require_chef_omnibus] = "12"
158
+
159
+ provisioner.install_command.must_match regexify(
160
+ "sudo -E sh /tmp/install.sh -v 12")
161
+ provisioner.install_command.must_match regexify(
162
+ "Installing Chef Omnibus (12)", :partial_line)
163
+ end
164
+
165
+ it "will install a downcaased version string of chef, if necessary" do
166
+ config[:require_chef_omnibus] = "10.1.0.RC.1"
167
+
168
+ provisioner.install_command.must_match regexify(
169
+ "sudo -E sh /tmp/install.sh -v 10.1.0.rc.1")
170
+ provisioner.install_command.must_match regexify(
171
+ "Installing Chef Omnibus (10.1.0.rc.1)", :partial_line)
172
+ end
173
+
174
+ it "will install the latest of chef, if necessary" do
175
+ config[:require_chef_omnibus] = "latest"
176
+
177
+ provisioner.install_command.must_match regexify(
178
+ "sudo -E sh /tmp/install.sh ")
179
+ provisioner.install_command.must_match regexify(
180
+ "Installing Chef Omnibus (always install latest version)",
181
+ :partial_line
182
+ )
183
+ end
184
+
185
+ it "will install a of chef, unless it exists" do
186
+ config[:require_chef_omnibus] = true
187
+
188
+ provisioner.install_command.must_match regexify(
189
+ "sudo -E sh /tmp/install.sh ")
190
+ provisioner.install_command.must_match regexify(
191
+ "Installing Chef Omnibus (install only if missing)", :partial_line)
192
+ end
193
+
194
+ it "will pass install options, when given" do
195
+ config[:chef_omnibus_install_options] = "-P chefdk"
196
+
197
+ provisioner.install_command.must_match regexify(
198
+ "sudo -E sh /tmp/install.sh -P chefdk")
199
+ provisioner.install_command.must_match regexify(
200
+ "Installing Chef Omnibus (install only if missing)", :partial_line)
201
+ end
202
+
203
+ end
204
+
205
+ describe "#init_command" do
206
+
207
+ it "uses bourne shell" do
208
+ provisioner.init_command.must_match(/\Ash -c '$/)
209
+ provisioner.init_command.must_match(/'\Z/)
210
+ end
211
+
212
+ it "uses sudo for rm when configured" do
213
+ config[:sudo] = true
214
+
215
+ provisioner.init_command.
216
+ must_match regexify("sudo -E rm -rf ", :partial_line)
217
+ end
218
+
219
+ it "does not use sudo for rm when configured" do
220
+ config[:sudo] = false
221
+
222
+ provisioner.init_command.
223
+ must_match regexify("rm -rf ", :partial_line)
224
+ provisioner.init_command.
225
+ wont_match regexify("sudo -E rm -rf ", :partial_line)
226
+ end
227
+
228
+ %w[cookbooks data data_bags environments roles clients].each do |dir|
229
+ it "removes the #{dir} directory" do
230
+ config[:root_path] = "/route"
231
+
232
+ provisioner.init_command.must_match %r{rm -rf\b.*\s+/route/#{dir}\s+}
233
+ end
234
+ end
235
+
236
+ it "creates :root_path directory" do
237
+ config[:root_path] = "/root/path"
238
+
239
+ provisioner.init_command.must_match regexify("mkdir -p /root/path")
240
+ end
241
+ end
242
+
243
+ describe "#create_sandbox" do
244
+
245
+ before do
246
+ @root = Dir.mktmpdir
247
+ config[:kitchen_root] = @root
248
+ end
249
+
250
+ after do
251
+ FileUtils.remove_entry(@root)
252
+ begin
253
+ provisioner.cleanup_sandbox
254
+ rescue # rubocop:disable Lint/HandleExceptions
255
+ end
256
+ end
257
+
258
+ let(:provisioner) do
259
+ Class.new(Kitchen::Provisioner::ChefBase) {
260
+ default_config :generic_rb, {}
261
+
262
+ def create_sandbox
263
+ super
264
+
265
+ data = default_config_rb.merge(config[:generic_rb])
266
+ File.open(File.join(sandbox_path, "generic.rb"), "wb") do |file|
267
+ file.write(format_config_file(data))
268
+ end
269
+ end
270
+ }.new(config).finalize_config!(instance)
271
+ end
272
+
273
+ describe "json file" do
274
+
275
+ let(:json) { JSON.parse(IO.read(sandbox_path("dna.json"))) }
276
+
277
+ it "creates a json file with node attributes" do
278
+ config[:attributes] = { "one" => { "two" => "three" } }
279
+ provisioner.create_sandbox
280
+
281
+ json["one"].must_equal("two" => "three")
282
+ end
283
+
284
+ it "creates a json file with run_list" do
285
+ config[:run_list] = %w[alpha bravo charlie]
286
+ provisioner.create_sandbox
287
+
288
+ json["run_list"].must_equal %w[alpha bravo charlie]
289
+ end
290
+
291
+ it "creates a json file with an empty run_list" do
292
+ config[:run_list] = []
293
+ provisioner.create_sandbox
294
+
295
+ json["run_list"].must_equal []
296
+ end
297
+
298
+ it "logs a message on info" do
299
+ provisioner.create_sandbox
300
+
301
+ logged_output.string.must_match info_line("Preparing dna.json")
302
+ end
303
+
304
+ it "logs a message on debug" do
305
+ config[:run_list] = ["yo"]
306
+ provisioner.create_sandbox
307
+
308
+ logged_output.string.
309
+ must_match debug_line(%|Creating dna.json from {:run_list=>["yo"]}|)
310
+ end
311
+ end
312
+
313
+ it "creates a cache directory" do
314
+ provisioner.create_sandbox
315
+
316
+ sandbox_path("cache").directory?.must_equal true
317
+ end
318
+
319
+ %w[data data_bags environments nodes roles clients].each do |thing|
320
+ describe "#{thing} files" do
321
+
322
+ before do
323
+ create_files_under("#{config[:kitchen_root]}/my_#{thing}")
324
+ config[:"#{thing}_path"] = "#{config[:kitchen_root]}/my_#{thing}"
325
+ end
326
+
327
+ it "skips directory creation if :#{thing}_path is not set" do
328
+ config[:"#{thing}_path"] = nil
329
+ provisioner.create_sandbox
330
+
331
+ sandbox_path(thing).directory?.must_equal false
332
+ end
333
+
334
+ it "copies tree from :#{thing}_path into sandbox" do
335
+ provisioner.create_sandbox
336
+
337
+ sandbox_path("#{thing}/alpha.txt").file?.must_equal true
338
+ IO.read(sandbox_path("#{thing}/alpha.txt")).must_equal "stuff"
339
+ sandbox_path("#{thing}/sub").directory?.must_equal true
340
+ sandbox_path("#{thing}/sub/bravo.txt").file?.must_equal true
341
+ IO.read(sandbox_path("#{thing}/sub/bravo.txt")).must_equal "junk"
342
+ end
343
+
344
+ it "logs a message on info" do
345
+ provisioner.create_sandbox
346
+
347
+ logged_output.string.must_match info_line("Preparing #{thing}")
348
+ end
349
+
350
+ it "logs a message on debug" do
351
+ provisioner.create_sandbox
352
+
353
+ logged_output.string.must_match debug_line(
354
+ "Using #{thing} from #{config[:kitchen_root]}/my_#{thing}")
355
+ end
356
+ end
357
+ end
358
+
359
+ describe "secret files" do
360
+
361
+ before do
362
+ config[:encrypted_data_bag_secret_key_path] =
363
+ "#{config[:kitchen_root]}/my_secret"
364
+ File.open("#{config[:kitchen_root]}/my_secret", "wb") do |file|
365
+ file.write("p@ss")
366
+ end
367
+ end
368
+
369
+ it "skips file if :encrypted_data_bag_secret_key_path is not set" do
370
+ config[:encrypted_data_bag_secret_key_path] = nil
371
+ provisioner.create_sandbox
372
+
373
+ sandbox_path("encrypted_data_bag_secret").file?.must_equal false
374
+ end
375
+
376
+ it "copies file from :encrypted_data_bag_secret_key_path into sandbox" do
377
+ provisioner.create_sandbox
378
+
379
+ sandbox_path("encrypted_data_bag_secret").file?.must_equal true
380
+ IO.read(sandbox_path("encrypted_data_bag_secret")).must_equal "p@ss"
381
+ end
382
+
383
+ it "logs a message on info" do
384
+ provisioner.create_sandbox
385
+
386
+ logged_output.string.must_match info_line("Preparing secret")
387
+ end
388
+
389
+ it "logs a message on debug" do
390
+ provisioner.create_sandbox
391
+
392
+ logged_output.string.must_match debug_line(
393
+ "Using secret from #{config[:kitchen_root]}/my_secret")
394
+ end
395
+ end
396
+
397
+ describe "cookbooks" do
398
+
399
+ let(:kitchen_root) { config[:kitchen_root] }
400
+
401
+ describe "with a cookbooks/ directory under kitchen_root" do
402
+
403
+ it "copies cookbooks/" do
404
+ create_cookbook("#{kitchen_root}/cookbooks/epache")
405
+ create_cookbook("#{kitchen_root}/cookbooks/jahva")
406
+ provisioner.create_sandbox
407
+
408
+ sandbox_path("cookbooks/epache").directory?.must_equal true
409
+ sandbox_path("cookbooks/epache/recipes/default.rb").
410
+ file?.must_equal true
411
+ sandbox_path("cookbooks/jahva").directory?.must_equal true
412
+ sandbox_path("cookbooks/jahva/recipes/default.rb").
413
+ file?.must_equal true
414
+ end
415
+
416
+ it "copies from kitchen_root as cookbook if it contains metadata.rb" do
417
+ File.open("#{kitchen_root}/metadata.rb", "wb") do |file|
418
+ file.write("name 'wat'")
419
+ end
420
+ create_cookbook("#{kitchen_root}/cookbooks/bk")
421
+ provisioner.create_sandbox
422
+
423
+ sandbox_path("cookbooks/bk").directory?.must_equal true
424
+ sandbox_path("cookbooks/wat").directory?.must_equal true
425
+ sandbox_path("cookbooks/wat/metadata.rb").file?.must_equal true
426
+ end
427
+
428
+ it "copies site-cookbooks/ if it exists" do
429
+ create_cookbook("#{kitchen_root}/cookbooks/upstream")
430
+ create_cookbook("#{kitchen_root}/site-cookbooks/mine")
431
+ provisioner.create_sandbox
432
+
433
+ sandbox_path("cookbooks/upstream").directory?.must_equal true
434
+ sandbox_path("cookbooks/mine").directory?.must_equal true
435
+ sandbox_path("cookbooks/mine/attributes/all.rb").file?.must_equal true
436
+ end
437
+
438
+ it "logs a message on info for cookbooks/ directory" do
439
+ create_cookbook("#{kitchen_root}/cookbooks/epache")
440
+ provisioner.create_sandbox
441
+
442
+ logged_output.string.must_match info_line(
443
+ "Preparing cookbooks from project directory")
444
+ end
445
+
446
+ it "logs a meesage on debug for cookbooks/ directory" do
447
+ create_cookbook("#{kitchen_root}/cookbooks/epache")
448
+ provisioner.create_sandbox
449
+
450
+ logged_output.string.must_match debug_line(
451
+ "Using cookbooks from #{kitchen_root}/cookbooks")
452
+ end
453
+
454
+ it "logs a message on info for site-cookbooks/ directory" do
455
+ create_cookbook("#{kitchen_root}/cookbooks/epache")
456
+ create_cookbook("#{kitchen_root}/site-cookbooks/mine")
457
+ provisioner.create_sandbox
458
+
459
+ logged_output.string.must_match info_line(
460
+ "Preparing site-cookbooks from project directory")
461
+ end
462
+
463
+ it "logs a meesage on debug for site-cookbooks/ directory" do
464
+ create_cookbook("#{kitchen_root}/cookbooks/epache")
465
+ create_cookbook("#{kitchen_root}/site-cookbooks/mine")
466
+ provisioner.create_sandbox
467
+
468
+ logged_output.string.must_match debug_line(
469
+ "Using cookbooks from #{kitchen_root}/site-cookbooks")
470
+ end
471
+ end
472
+
473
+ describe "with a cookbook as the project" do
474
+
475
+ before do
476
+ File.open("#{kitchen_root}/metadata.rb", "wb") do |file|
477
+ file.write("name 'wat'")
478
+ end
479
+ end
480
+
481
+ it "copies from kitchen_root as cookbook if it contains metadata.rb" do
482
+ provisioner.create_sandbox
483
+
484
+ sandbox_path("cookbooks/wat").directory?.must_equal true
485
+ sandbox_path("cookbooks/wat/metadata.rb").file?.must_equal true
486
+ end
487
+
488
+ it "logs a message on info" do
489
+ provisioner.create_sandbox
490
+
491
+ logged_output.string.must_match info_line(
492
+ "Preparing current project directory as a cookbook")
493
+ end
494
+
495
+ it "logs a meesage on debug" do
496
+ provisioner.create_sandbox
497
+
498
+ logged_output.string.must_match debug_line(
499
+ "Using metadata.rb from #{kitchen_root}/metadata.rb")
500
+ end
501
+
502
+ it "raises a UserError is name cannot be determined from metadata.rb" do
503
+ File.open("#{kitchen_root}/metadata.rb", "wb") do |file|
504
+ file.write("nameeeeee 'wat'")
505
+ end
506
+
507
+ proc { provisioner.create_sandbox }.must_raise Kitchen::UserError
508
+ end
509
+ end
510
+
511
+ describe "with no referenced cookbooks" do
512
+
513
+ it "makes a fake cookbook" do
514
+ name = File.basename(@root)
515
+ provisioner.create_sandbox
516
+
517
+ sandbox_path("cookbooks/#{name}").directory?.must_equal true
518
+ sandbox_path("cookbooks/#{name}/metadata.rb").file?.must_equal true
519
+ IO.read(sandbox_path("cookbooks/#{name}/metadata.rb")).
520
+ must_equal %{name "#{name}"\n}
521
+ end
522
+
523
+ it "logs a warning" do
524
+ provisioner.create_sandbox
525
+
526
+ logged_output.string.must_match regexify(
527
+ "Berksfile, Cheffile, cookbooks/, or metadata.rb not found",
528
+ :partial_line
529
+ )
530
+ end
531
+ end
532
+
533
+ describe "with a Berksfile under kitchen_root" do
534
+
535
+ let(:resolver) { stub(:resolve => true) }
536
+
537
+ before do
538
+ File.open("#{kitchen_root}/Berksfile", "wb") do |file|
539
+ file.write("cookbook 'wat'")
540
+ end
541
+ Kitchen::Provisioner::Chef::Berkshelf.stubs(:new).returns(resolver)
542
+ end
543
+
544
+ it "raises a UserError if Berkshelf library can't be loaded" do
545
+ proc { provisioner }.must_raise Kitchen::UserError
546
+ end
547
+
548
+ it "logs on debug that Berkshelf is loading" do
549
+ Kitchen::Provisioner::Chef::Berkshelf.stubs(:load!)
550
+ provisioner
551
+
552
+ logged_output.string.must_match debug_line(
553
+ "Berksfile found at #{kitchen_root}/Berksfile, loading Berkshelf")
554
+ end
555
+
556
+ it "uses Berkshelf" do
557
+ Kitchen::Provisioner::Chef::Berkshelf.stubs(:load!)
558
+ resolver.expects(:resolve)
559
+
560
+ provisioner.create_sandbox
561
+ end
562
+
563
+ it "uses Kitchen.mutex for resolving" do
564
+ Kitchen::Provisioner::Chef::Berkshelf.stubs(:load!)
565
+ Kitchen.mutex.expects(:synchronize)
566
+
567
+ provisioner.create_sandbox
568
+ end
569
+ end
570
+
571
+ describe "with a Cheffile under kitchen_root" do
572
+
573
+ let(:resolver) { stub(:resolve => true) }
574
+
575
+ before do
576
+ File.open("#{kitchen_root}/Cheffile", "wb") do |file|
577
+ file.write("cookbook 'wat'")
578
+ end
579
+ Kitchen::Provisioner::Chef::Librarian.stubs(:new).returns(resolver)
580
+ end
581
+
582
+ it "raises a UserError if Librarian library can't be loaded" do
583
+ proc { provisioner }.must_raise Kitchen::UserError
584
+ end
585
+
586
+ it "logs on debug that Berkshelf is loading" do
587
+ Kitchen::Provisioner::Chef::Librarian.stubs(:load!)
588
+ provisioner
589
+
590
+ logged_output.string.must_match debug_line(
591
+ "Cheffile found at #{kitchen_root}/Cheffile, loading Librarian-Chef"
592
+ )
593
+ end
594
+
595
+ it "uses Librarian" do
596
+ Kitchen::Provisioner::Chef::Librarian.stubs(:load!)
597
+ resolver.expects(:resolve)
598
+
599
+ provisioner.create_sandbox
600
+ end
601
+
602
+ it "uses Kitchen.mutex for resolving" do
603
+ Kitchen::Provisioner::Chef::Librarian.stubs(:load!)
604
+ Kitchen.mutex.expects(:synchronize)
605
+
606
+ provisioner.create_sandbox
607
+ end
608
+ end
609
+
610
+ describe "filtering cookbooks files" do
611
+
612
+ it "retains all useful cookbook files" do
613
+ create_full_cookbook("#{kitchen_root}/cookbooks/full")
614
+ provisioner.create_sandbox
615
+
616
+ full_cookbook_files.each do |file|
617
+ sandbox_path("cookbooks/full/#{file}").file?.must_equal true
618
+ end
619
+ end
620
+
621
+ it "strips extra cookbook files" do
622
+ extras = %w[
623
+ .gitignore tmp/librarian chefignore .git/info/excludes
624
+ cookbooks/another/metadata.rb CONTRIBUTING.md metadata.py
625
+ ]
626
+
627
+ create_full_cookbook("#{kitchen_root}/cookbooks/full")
628
+ extras.each do |file|
629
+ create_file("#{kitchen_root}/cookbooks/full/#{file}")
630
+ end
631
+ provisioner.create_sandbox
632
+
633
+ extras.each do |file|
634
+ sandbox_path("cookbooks/full/#{file}").file?.must_equal false
635
+ end
636
+ end
637
+
638
+ it "logs on info" do
639
+ create_full_cookbook("#{kitchen_root}/cookbooks/full")
640
+ provisioner.create_sandbox
641
+
642
+ logged_output.string.must_match info_line(
643
+ "Removing non-cookbook files before transfer")
644
+ end
645
+ end
646
+
647
+ describe "Chef config files" do
648
+
649
+ let(:file) do
650
+ IO.read(sandbox_path("generic.rb")).lines.map(&:chomp)
651
+ end
652
+
653
+ it "#create_sanbox creates a generic.rb" do
654
+ provisioner.create_sandbox
655
+
656
+ sandbox_path("generic.rb").file?.must_equal true
657
+ end
658
+
659
+ describe "defaults" do
660
+
661
+ before { provisioner.create_sandbox }
662
+
663
+ it "sets node_name to the instance name" do
664
+ file.must_include %{node_name "#{instance.name}"}
665
+ end
666
+
667
+ it "sets checksum_path" do
668
+ file.must_include %{checksum_path "/tmp/kitchen/checksums"}
669
+ end
670
+
671
+ it "sets file_backup_path" do
672
+ file.must_include %{file_backup_path "/tmp/kitchen/backup"}
673
+ end
674
+
675
+ it "sets cookbook_path" do
676
+ file.must_include %{cookbook_path } +
677
+ %{["/tmp/kitchen/cookbooks", "/tmp/kitchen/site-cookbooks"]}
678
+ end
679
+
680
+ it "sets data_bag_path" do
681
+ file.must_include %{data_bag_path "/tmp/kitchen/data_bags"}
682
+ end
683
+
684
+ it "sets environment_path" do
685
+ file.must_include %{environment_path "/tmp/kitchen/environments"}
686
+ end
687
+
688
+ it "sets node_path" do
689
+ file.must_include %{node_path "/tmp/kitchen/nodes"}
690
+ end
691
+
692
+ it "sets role_path" do
693
+ file.must_include %{role_path "/tmp/kitchen/roles"}
694
+ end
695
+
696
+ it "sets client_path" do
697
+ file.must_include %{client_path "/tmp/kitchen/clients"}
698
+ end
699
+
700
+ it "sets user_path" do
701
+ file.must_include %{user_path "/tmp/kitchen/users"}
702
+ end
703
+
704
+ it "sets validation_key" do
705
+ file.must_include %{validation_key "/tmp/kitchen/validation.pem"}
706
+ end
707
+
708
+ it "sets client_key" do
709
+ file.must_include %{client_key "/tmp/kitchen/client.pem"}
710
+ end
711
+
712
+ it "sets chef_server_url" do
713
+ file.must_include %{chef_server_url "http://127.0.0.1:8889"}
714
+ end
715
+
716
+ it "sets encrypted_data_bag_secret" do
717
+ file.must_include %{encrypted_data_bag_secret } +
718
+ %{"/tmp/kitchen/encrypted_data_bag_secret"}
719
+ end
720
+ end
721
+
722
+ it "supports overwriting defaults" do
723
+ config[:generic_rb] = {
724
+ :node_name => "eagles",
725
+ :user_path => "/a/b/c/u",
726
+ :chef_server_url => "https://whereever.io"
727
+ }
728
+ provisioner.create_sandbox
729
+
730
+ file.must_include %{node_name "eagles"}
731
+ file.must_include %{user_path "/a/b/c/u"}
732
+ file.must_include %{chef_server_url "https://whereever.io"}
733
+ end
734
+
735
+ it " supports adding new configuration" do
736
+ config[:generic_rb] = {
737
+ :dark_secret => "golang"
738
+ }
739
+ provisioner.create_sandbox
740
+
741
+ file.must_include %{dark_secret "golang"}
742
+ end
743
+ end
744
+
745
+ def create_cookbook(path)
746
+ %w[metadata.rb attributes/all.rb recipes/default.rb].each do |file|
747
+ create_file(File.join(path, file))
748
+ end
749
+ end
750
+
751
+ def full_cookbook_files
752
+ %w[
753
+ README.org metadata.rb attributes/all.rb definitions/def.rb
754
+ files/default/config.conf libraries/one.rb libraries/two.rb
755
+ providers/sweet.rb recipes/default.rb resources/sweet.rb
756
+ templates/ubuntu/12.04/nginx.conf.erb
757
+ ]
758
+ end
759
+
760
+ def create_full_cookbook(path)
761
+ full_cookbook_files.each { |file| create_file(File.join(path, file)) }
762
+ end
763
+
764
+ def create_file(path)
765
+ FileUtils.mkdir_p(File.dirname(path))
766
+ File.open(path, "wb") { |f| f.write(path) }
767
+ end
768
+ end
769
+
770
+ def sandbox_path(path)
771
+ Pathname.new(provisioner.sandbox_path).join(path)
772
+ end
773
+
774
+ def create_files_under(path)
775
+ FileUtils.mkdir_p(File.join(path, "sub"))
776
+ File.open(File.join(path, "alpha.txt"), "wb") do |file|
777
+ file.write("stuff")
778
+ end
779
+ File.open(File.join(path, "sub", "bravo.txt"), "wb") do |file|
780
+ file.write("junk")
781
+ end
782
+ end
783
+
784
+ def info_line(msg)
785
+ %r{^I, .* : #{Regexp.escape(msg)}$}
786
+ end
787
+
788
+ def debug_line(msg)
789
+ %r{^D, .* : #{Regexp.escape(msg)}$}
790
+ end
791
+ end
792
+
793
+ def regexify(str, line = :whole_line)
794
+ r = Regexp.escape(str)
795
+ r = "^\s*#{r}$" if line == :whole_line
796
+ Regexp.new(r)
797
+ end
798
+ end