test-kitchen 1.3.1 → 1.4.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +2 -0
  3. data/.gitignore +4 -0
  4. data/CHANGELOG.md +45 -0
  5. data/Rakefile +15 -0
  6. data/features/kitchen_action_commands.feature +12 -9
  7. data/features/kitchen_defaults.feature +38 -0
  8. data/features/kitchen_init_command.feature +0 -1
  9. data/features/kitchen_list_command.feature +2 -2
  10. data/features/kitchen_login_command.feature +7 -1
  11. data/features/kitchen_test_command.feature +4 -4
  12. data/lib/kitchen.rb +40 -11
  13. data/lib/kitchen/cli.rb +38 -22
  14. data/lib/kitchen/command/list.rb +5 -2
  15. data/lib/kitchen/config.rb +45 -18
  16. data/lib/kitchen/configurable.rb +137 -1
  17. data/lib/kitchen/data_munger.rb +248 -17
  18. data/lib/kitchen/driver.rb +1 -1
  19. data/lib/kitchen/driver/base.rb +1 -83
  20. data/lib/kitchen/driver/dummy.rb +0 -5
  21. data/lib/kitchen/driver/ssh_base.rb +177 -22
  22. data/lib/kitchen/instance.rb +140 -20
  23. data/lib/kitchen/logger.rb +43 -8
  24. data/lib/kitchen/login_command.rb +14 -5
  25. data/lib/kitchen/platform.rb +19 -0
  26. data/lib/kitchen/provisioner.rb +5 -3
  27. data/lib/kitchen/provisioner/base.rb +46 -48
  28. data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -0
  29. data/lib/kitchen/provisioner/chef_base.rb +179 -286
  30. data/lib/kitchen/provisioner/chef_solo.rb +11 -5
  31. data/lib/kitchen/provisioner/chef_zero.rb +108 -94
  32. data/lib/kitchen/provisioner/dummy.rb +47 -0
  33. data/lib/kitchen/provisioner/shell.rb +45 -12
  34. data/lib/kitchen/rake_tasks.rb +1 -1
  35. data/lib/kitchen/ssh.rb +1 -1
  36. data/lib/kitchen/thor_tasks.rb +1 -1
  37. data/lib/kitchen/transport.rb +54 -0
  38. data/lib/kitchen/transport/base.rb +146 -0
  39. data/lib/kitchen/transport/dummy.rb +75 -0
  40. data/lib/kitchen/transport/ssh.rb +325 -0
  41. data/lib/kitchen/transport/winrm.rb +508 -0
  42. data/lib/kitchen/transport/winrm/command_executor.rb +188 -0
  43. data/lib/kitchen/transport/winrm/file_transporter.rb +454 -0
  44. data/lib/kitchen/transport/winrm/logging.rb +50 -0
  45. data/lib/kitchen/transport/winrm/template.rb +74 -0
  46. data/lib/kitchen/transport/winrm/tmp_zip.rb +187 -0
  47. data/lib/kitchen/verifier.rb +55 -0
  48. data/lib/kitchen/verifier/base.rb +191 -0
  49. data/lib/kitchen/verifier/busser.rb +266 -0
  50. data/lib/kitchen/verifier/dummy.rb +75 -0
  51. data/lib/kitchen/version.rb +1 -1
  52. data/spec/kitchen/cli_spec.rb +56 -0
  53. data/spec/kitchen/config_spec.rb +61 -20
  54. data/spec/kitchen/configurable_spec.rb +327 -1
  55. data/spec/kitchen/data_munger_spec.rb +777 -14
  56. data/spec/kitchen/driver/base_spec.rb +7 -38
  57. data/spec/kitchen/driver/dummy_spec.rb +0 -29
  58. data/spec/kitchen/driver/ssh_base_spec.rb +580 -236
  59. data/spec/kitchen/driver_spec.rb +1 -0
  60. data/spec/kitchen/instance_spec.rb +383 -83
  61. data/spec/kitchen/login_command_spec.rb +29 -10
  62. data/spec/kitchen/platform_spec.rb +58 -2
  63. data/spec/kitchen/provisioner/base_spec.rb +170 -18
  64. data/spec/kitchen/provisioner/chef_base_spec.rb +454 -104
  65. data/spec/kitchen/provisioner/chef_solo_spec.rb +307 -104
  66. data/spec/kitchen/provisioner/chef_zero_spec.rb +561 -230
  67. data/spec/kitchen/provisioner/dummy_spec.rb +91 -0
  68. data/spec/kitchen/provisioner/shell_spec.rb +158 -56
  69. data/spec/kitchen/provisioner_spec.rb +37 -0
  70. data/spec/kitchen/ssh_spec.rb +19 -19
  71. data/spec/kitchen/transport/base_spec.rb +89 -0
  72. data/spec/kitchen/transport/ssh_spec.rb +1147 -0
  73. data/spec/kitchen/transport/winrm/command_executor_spec.rb +400 -0
  74. data/spec/kitchen/transport/winrm/file_transporter_spec.rb +876 -0
  75. data/spec/kitchen/transport/winrm/logging_spec.rb +92 -0
  76. data/spec/kitchen/transport/winrm/template_spec.rb +51 -0
  77. data/spec/kitchen/transport/winrm/tmp_zip_spec.rb +132 -0
  78. data/spec/kitchen/transport/winrm_spec.rb +1069 -0
  79. data/spec/kitchen/transport_spec.rb +112 -0
  80. data/spec/kitchen/verifier/base_spec.rb +310 -0
  81. data/spec/kitchen/verifier/busser_spec.rb +540 -0
  82. data/spec/kitchen/verifier/dummy_spec.rb +91 -0
  83. data/spec/kitchen/verifier_spec.rb +120 -0
  84. data/spec/kitchen_spec.rb +7 -0
  85. data/spec/spec_helper.rb +8 -0
  86. data/spec/support/powershell_max_size_spec.rb +40 -0
  87. data/support/busser_install_command.ps1 +14 -0
  88. data/support/busser_install_command.sh +15 -0
  89. data/support/check_files.ps1.erb +48 -0
  90. data/support/chef_base_init_command.ps1 +18 -0
  91. data/support/chef_base_init_command.sh +2 -0
  92. data/support/chef_base_install_command.ps1 +76 -0
  93. data/support/chef_base_install_command.sh +137 -0
  94. data/support/chef_zero_prepare_command_legacy.ps1 +9 -0
  95. data/support/chef_zero_prepare_command_legacy.sh +10 -0
  96. data/support/decode_files.ps1.erb +61 -0
  97. data/test-kitchen.gemspec +2 -0
  98. metadata +97 -8
  99. data/lib/kitchen/busser.rb +0 -316
  100. data/spec/kitchen/busser_spec.rb +0 -490
  101. data/support/chef_helpers.sh +0 -16
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2015, 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
+ module Kitchen
20
+
21
+ module Transport
22
+
23
+ class Winrm < Kitchen::Transport::Base
24
+
25
+ # Mixin to use an optionally provided logger for logging.
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
28
+ module Logging
29
+
30
+ # Logs a message on the logger at the debug level, if a logger is
31
+ # present.
32
+ #
33
+ # @param msg [String] a message to log
34
+ # @yield evaluates and uses return value as message to log. If msg
35
+ # parameter is set, it will take precedence over the block.
36
+ def debug(msg = nil, &block)
37
+ return if logger.nil? || !logger.debug?
38
+ logger.debug("[#{log_subject}] " << (msg || block.call))
39
+ end
40
+
41
+ # The subject for log messages.
42
+ #
43
+ # @return [String] log subject
44
+ def log_subject
45
+ @log_subject ||= self.class.to_s.split("::").last
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,74 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2015, 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 "erb"
20
+ require "ostruct"
21
+
22
+ module Kitchen
23
+
24
+ module Transport
25
+
26
+ class Winrm < Kitchen::Transport::Base
27
+
28
+ # Wraps an ERb template which can be called multiple times with
29
+ # different binding contexts.
30
+ #
31
+ # @author Fletcher Nichol <fnichol@nichol.ca>
32
+ # @api private
33
+ class Template
34
+
35
+ # Initializes an ERb template using a file as the template source.
36
+ #
37
+ # @param file [String] path to an ERb template file
38
+ def initialize(file)
39
+ @erb = ERB.new(IO.read(file))
40
+ end
41
+
42
+ # Renders the template using a hash as context.
43
+ #
44
+ # @param vars [Hash] a hash used for context
45
+ # @return [String] the rendered template
46
+ def render(vars)
47
+ @erb.result(Context.for(vars))
48
+ end
49
+ alias_method :%, :render
50
+
51
+ # Internal class which wraps a binding context for rendering
52
+ # an ERb template.
53
+ #
54
+ # @author Fletcher Nichol <fnichol@nichol.ca>
55
+ # @api private
56
+ class Context < OpenStruct
57
+
58
+ # Creates a new binding context for a hash of data.
59
+ #
60
+ # @param vars [Hash] a hash used for context
61
+ # @return [Binding] a binding context for the given hash
62
+ def self.for(vars)
63
+ new(vars).my_binding
64
+ end
65
+
66
+ # @return [Binding] a binding context
67
+ def my_binding
68
+ binding
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,187 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2015, 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 "delegate"
20
+ require "pathname"
21
+ require "tempfile"
22
+ require "zip"
23
+
24
+ require "kitchen/transport/winrm/logging"
25
+
26
+ module Kitchen
27
+
28
+ module Transport
29
+
30
+ class Winrm < Kitchen::Transport::Base
31
+
32
+ # A temporary Zip file for a given directory.
33
+ #
34
+ # @author Fletcher Nichol <fnichol@nichol.ca>
35
+ class TmpZip
36
+
37
+ include Logging
38
+
39
+ # Contructs a new Zip file for the given directory.
40
+ #
41
+ # There are 2 ways to interpret the directory path:
42
+ #
43
+ # * If the directory has no path separator terminator, then the
44
+ # directory basename will be used as the base directory in the
45
+ # resulting zip file.
46
+ # * If the directory has a path separator terminator (such as `/` or
47
+ # `\\`), then the entries under the directory will be added to the
48
+ # resulting zip file.
49
+ #
50
+ # The following emaples assume a directory tree structure of:
51
+ #
52
+ # src
53
+ # |-- alpha.txt
54
+ # |-- beta.txt
55
+ # \-- sub
56
+ # \-- charlie.txt
57
+ #
58
+ # @example Including the base directory in the zip file
59
+ #
60
+ # TmpZip.new("/path/to/src")
61
+ # # produces a zip file with entries:
62
+ # # - src/alpha.txt
63
+ # # - src/beta.txt
64
+ # # - src/sub/charlie.txt
65
+ #
66
+ # @example Excluding the base directory in the zip file
67
+ #
68
+ # TmpZip.new("/path/to/src/")
69
+ # # produces a zip file with entries:
70
+ # # - alpha.txt
71
+ # # - beta.txt
72
+ # # - sub/charlie.txt
73
+ #
74
+ # @param dir [String,Pathname,#to_s] path to the directory
75
+ # @param logger [#debug,#debug?] an optional logger/ui object that
76
+ # responds to `#debug` and `#debug?` (default `nil`)
77
+ def initialize(dir, logger = nil)
78
+ @logger = logger
79
+ @dir = Pathname.new(dir)
80
+ @method = ::Zip::Entry::DEFLATED
81
+ @compression = Zlib::BEST_COMPRESSION
82
+ @zip_io = Tempfile.open(["tmpzip-", ".zip"], :binmode => true)
83
+ write_zip
84
+ @zip_io.close
85
+ end
86
+
87
+ # @return [Pathname] path to zip file
88
+ def path
89
+ Pathname.new(zip_io.path) if zip_io.path
90
+ end
91
+
92
+ # Unlinks (deletes) the zip file from the filesystem.
93
+ def unlink
94
+ zip_io.unlink
95
+ end
96
+
97
+ private
98
+
99
+ # @return [Integer] the compression used for Zip entries. Possible
100
+ # values are `Zlib::BEST_COMPRESSION`, `Zlib::DEFAULT_COMPRESSION`,
101
+ # and `Zlib::NO_COMPRESSION`.
102
+ # @api private
103
+ attr_reader :compression
104
+
105
+ # @return [Pathname] the directory used to create the Zip file
106
+ # @api private
107
+ attr_reader :dir
108
+
109
+ # @return [#debug] the logger
110
+ # @api private
111
+ attr_reader :logger
112
+
113
+ # @return [Integer] compression method used for Zip entries. Possible
114
+ # values are `Zip::Entry::DEFLATED` and `Zip::Entry::STORED`.
115
+ # @api private
116
+ attr_reader :method
117
+
118
+ # @return [IO] the Zip file IO
119
+ # @api private
120
+ attr_reader :zip_io
121
+
122
+ # @return [Pathname] the path segement to be stripped off Zip entries
123
+ # @api private
124
+ def dir_strip
125
+ @dir_strip ||= if dir.to_s.end_with?("/", "\\")
126
+ Pathname.new(dir.to_s.chop)
127
+ else
128
+ dir.dirname
129
+ end
130
+ end
131
+
132
+ # @return [Array<Pathname] all recursive files under the base
133
+ # directory, excluding directories
134
+ # @api private
135
+ def entries
136
+ Pathname.glob(dir.join("**/*")).delete_if(&:directory?).sort
137
+ end
138
+
139
+ # (see Logging.log_subject)
140
+ # @api private
141
+ def log_subject
142
+ @log_subject ||= [self.class.to_s.split("::").last, path].join("::")
143
+ end
144
+
145
+ # Adds all file entries to the Zip output stream.
146
+ #
147
+ # @param zos [Zip::OutputStream] zip output stream
148
+ # @api private
149
+ def produce_zip_entries(zos)
150
+ entries.each do |entry|
151
+ entry_path = entry.sub("#{dir_strip}/", "")
152
+ debug { "+++ Adding #{entry_path}" }
153
+ zos.put_next_entry(entry_path, nil, nil, method, compression)
154
+ entry.open("rb") { |src| IO.copy_stream(src, zos) }
155
+ end
156
+ debug { "=== All files added." }
157
+ end
158
+
159
+ # Writes out a temporary Zip file.
160
+ #
161
+ # @api private
162
+ def write_zip
163
+ debug { "Populating files" }
164
+ Zip::OutputStream.write_buffer(NoDupIO.new(zip_io)) do |zos|
165
+ produce_zip_entries(zos)
166
+ end
167
+ end
168
+
169
+ # Simple delegate wrapper to prevent `#dup` calls being made on IO
170
+ # objects. This is used to bypass an issue in the `Zip::Outputstream`
171
+ # constructor where an incoming IO is duplicated, leading to races
172
+ # on flushing the final stream to disk.
173
+ #
174
+ # @author Fletcher Nichol <fnichol@nichol.ca>
175
+ # @api private
176
+ class NoDupIO < SimpleDelegator
177
+
178
+ # @return [self] returns self and does *not* return a duplicate
179
+ # object
180
+ def dup
181
+ self
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,55 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2015, 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 "thor/util"
20
+
21
+ require "kitchen/errors"
22
+
23
+ module Kitchen
24
+
25
+ # A verifier is responsible for running tests post-converge to confirm that
26
+ # the instance is in a known/consistent state.
27
+ #
28
+ # @author Fletcher Nichol <fnichol@nichol.ca>
29
+ module Verifier
30
+
31
+ # Default verifier to use
32
+ DEFAULT_PLUGIN = "busser".freeze
33
+
34
+ # Returns an instance of a verifier given a plugin type string.
35
+ #
36
+ # @param plugin [String] a verifier plugin type, to be constantized
37
+ # @param config [Hash] a configuration hash to initialize the verifier
38
+ # @return [Verifier::Base] a verifier instance
39
+ # @raise [ClientError] if a verifier instance could not be created
40
+ def self.for_plugin(plugin, config)
41
+ first_load = require("kitchen/verifier/#{plugin}")
42
+
43
+ str_const = Thor::Util.camel_case(plugin)
44
+ klass = const_get(str_const)
45
+ object = klass.new(config)
46
+ object.verify_dependencies if first_load
47
+ object
48
+ rescue LoadError, NameError
49
+ raise ClientError,
50
+ "Could not load the '#{plugin}' verifier from the load path." \
51
+ " Please ensure that your transport is installed as a gem or" \
52
+ " included in your Gemfile if using Bundler."
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,191 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2015, 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 "kitchen/errors"
20
+ require "kitchen/configurable"
21
+ require "kitchen/logging"
22
+
23
+ module Kitchen
24
+
25
+ module Verifier
26
+
27
+ # Base class for a verifier.
28
+ #
29
+ # @author Fletcher Nichol <fnichol@nichol.ca>
30
+ class Base
31
+
32
+ include Configurable
33
+ include Logging
34
+
35
+ default_config :http_proxy, nil
36
+
37
+ default_config :https_proxy, nil
38
+
39
+ default_config :root_path do |verifier|
40
+ verifier.windows_os? ? "$env:TEMP\\verifier" : "/tmp/verifier"
41
+ end
42
+
43
+ default_config :sudo do |verifier|
44
+ verifier.windows_os? ? nil : true
45
+ end
46
+
47
+ default_config(:suite_name) { |busser| busser.instance.suite.name }
48
+
49
+ # Creates a new Verifier object using the provided configuration data
50
+ # which will be merged with any default configuration.
51
+ #
52
+ # @param config [Hash] provided verifier configuration
53
+ def initialize(config = {})
54
+ init_config(config)
55
+ end
56
+
57
+ # Runs the verifier on the instance.
58
+ #
59
+ # @param state [Hash] mutable instance state
60
+ # @raise [ActionFailed] if the action could not be completed
61
+ def call(state)
62
+ create_sandbox
63
+ sandbox_dirs = Dir.glob(File.join(sandbox_path, "*"))
64
+
65
+ instance.transport.connection(state) do |conn|
66
+ conn.execute(install_command)
67
+ conn.execute(init_command)
68
+ info("Transferring files to #{instance.to_str}")
69
+ conn.upload(sandbox_dirs, config[:root_path])
70
+ debug("Transfer complete")
71
+ conn.execute(prepare_command)
72
+ conn.execute(run_command)
73
+ end
74
+ rescue Kitchen::Transport::TransportFailed => ex
75
+ raise ActionFailed, ex.message
76
+ ensure
77
+ cleanup_sandbox
78
+ end
79
+
80
+ # Deletes the sandbox path. Without calling this method, the sandbox path
81
+ # will persist after the process terminates. In other words, cleanup is
82
+ # explicit. This method is safe to call multiple times.
83
+ def cleanup_sandbox
84
+ return if sandbox_path.nil?
85
+
86
+ debug("Cleaning up local sandbox in #{sandbox_path}")
87
+ FileUtils.rmtree(sandbox_path)
88
+ end
89
+
90
+ # Creates a temporary directory on the local workstation into which
91
+ # verifier related files and directories can be copied or created. The
92
+ # contents of this directory will be copied over to the instance before
93
+ # invoking the verifier's run command. After this method completes, it
94
+ # is expected that the contents of the sandbox is complete and ready for
95
+ # copy to the remote instance.
96
+ #
97
+ # **Note:** any subclasses would be well advised to call super first when
98
+ # overriding this method, for example:
99
+ #
100
+ # @example overriding `#create_sandbox`
101
+ #
102
+ # class MyVerifier < Kitchen::Verifier::Base
103
+ # def create_sandbox
104
+ # super
105
+ # # any further file copies, preparations, etc.
106
+ # end
107
+ # end
108
+ def create_sandbox
109
+ @sandbox_path = Dir.mktmpdir("#{instance.name}-sandbox-")
110
+ File.chmod(0755, sandbox_path)
111
+ info("Preparing files for transfer")
112
+ debug("Creating local sandbox in #{sandbox_path}")
113
+ end
114
+
115
+ # Generates a command string which will install and configure the
116
+ # verifier software on an instance. If no work is required, then `nil`
117
+ # will be returned.
118
+ #
119
+ # @return [String] a command string
120
+ def install_command
121
+ end
122
+
123
+ # Generates a command string which will perform any data initialization
124
+ # or configuration required after the verifier software is installed
125
+ # but before the sandbox has been transferred to the instance. If no work
126
+ # is required, then `nil` will be returned.
127
+ #
128
+ # @return [String] a command string
129
+ def init_command
130
+ end
131
+
132
+ # Generates a command string which will perform any commands or
133
+ # configuration required just before the main verifier run command but
134
+ # after the sandbox has been transferred to the instance. If no work is
135
+ # required, then `nil` will be returned.
136
+ #
137
+ # @return [String] a command string
138
+ def prepare_command
139
+ end
140
+
141
+ # Generates a command string which will invoke the main verifier
142
+ # command on the prepared instance. If no work is required, then `nil`
143
+ # will be returned.
144
+ #
145
+ # @return [String] a command string
146
+ def run_command
147
+ end
148
+
149
+ # Returns the absolute path to the sandbox directory or raises an
150
+ # exception if `#create_sandbox` has not yet been called.
151
+ #
152
+ # @return [String] the absolute path to the sandbox directory
153
+ # @raise [ClientError] if the sandbox directory has no yet been created
154
+ # by calling `#create_sandbox`
155
+ def sandbox_path
156
+ @sandbox_path || (raise ClientError, "Sandbox directory has not yet " \
157
+ "been created. Please run #{self.class}#create_sandox before " \
158
+ "trying to access the path.")
159
+ end
160
+
161
+ private
162
+
163
+ # Builds a complete command given a variables String preamble and a file
164
+ # containing shell code.
165
+ #
166
+ # @param vars [String] shell variables, as a String
167
+ # @param file [String] file basename (without extension) containing
168
+ # shell code
169
+ # @return [String] command
170
+ # @api private
171
+ def shell_code_from_file(vars, file)
172
+ src_file = File.join(
173
+ File.dirname(__FILE__),
174
+ %w[.. .. .. support],
175
+ file + (powershell_shell? ? ".ps1" : ".sh")
176
+ )
177
+
178
+ wrap_shell_code([vars, "", IO.read(src_file)].join("\n"))
179
+ end
180
+
181
+ # Conditionally prefixes a command with a sudo command.
182
+ #
183
+ # @param command [String] command to be prefixed
184
+ # @return [String] the command, conditionaly prefixed with sudo
185
+ # @api private
186
+ def sudo(script)
187
+ config[:sudo] ? "sudo -E #{script}" : script
188
+ end
189
+ end
190
+ end
191
+ end