test-kitchen-rsync 3.0.0.pre.1

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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +21 -0
  3. data/LICENSE +15 -0
  4. data/Rakefile +53 -0
  5. data/bin/zl-kitchen +11 -0
  6. data/lib/kitchen/base64_stream.rb +48 -0
  7. data/lib/kitchen/chef_utils_wiring.rb +40 -0
  8. data/lib/kitchen/cli.rb +413 -0
  9. data/lib/kitchen/collection.rb +52 -0
  10. data/lib/kitchen/color.rb +63 -0
  11. data/lib/kitchen/command/action.rb +41 -0
  12. data/lib/kitchen/command/console.rb +54 -0
  13. data/lib/kitchen/command/diagnose.rb +84 -0
  14. data/lib/kitchen/command/doctor.rb +39 -0
  15. data/lib/kitchen/command/exec.rb +37 -0
  16. data/lib/kitchen/command/list.rb +148 -0
  17. data/lib/kitchen/command/login.rb +39 -0
  18. data/lib/kitchen/command/package.rb +32 -0
  19. data/lib/kitchen/command/sink.rb +50 -0
  20. data/lib/kitchen/command/test.rb +47 -0
  21. data/lib/kitchen/command.rb +207 -0
  22. data/lib/kitchen/config.rb +344 -0
  23. data/lib/kitchen/configurable.rb +616 -0
  24. data/lib/kitchen/data_munger.rb +1024 -0
  25. data/lib/kitchen/diagnostic.rb +138 -0
  26. data/lib/kitchen/driver/base.rb +133 -0
  27. data/lib/kitchen/driver/dummy.rb +105 -0
  28. data/lib/kitchen/driver/exec.rb +70 -0
  29. data/lib/kitchen/driver/proxy.rb +70 -0
  30. data/lib/kitchen/driver/ssh_base.rb +351 -0
  31. data/lib/kitchen/driver.rb +40 -0
  32. data/lib/kitchen/errors.rb +243 -0
  33. data/lib/kitchen/generator/init.rb +254 -0
  34. data/lib/kitchen/instance.rb +726 -0
  35. data/lib/kitchen/lazy_hash.rb +148 -0
  36. data/lib/kitchen/lifecycle_hook/base.rb +78 -0
  37. data/lib/kitchen/lifecycle_hook/local.rb +53 -0
  38. data/lib/kitchen/lifecycle_hook/remote.rb +39 -0
  39. data/lib/kitchen/lifecycle_hooks.rb +92 -0
  40. data/lib/kitchen/loader/yaml.rb +377 -0
  41. data/lib/kitchen/logger.rb +422 -0
  42. data/lib/kitchen/logging.rb +52 -0
  43. data/lib/kitchen/login_command.rb +49 -0
  44. data/lib/kitchen/metadata_chopper.rb +49 -0
  45. data/lib/kitchen/platform.rb +64 -0
  46. data/lib/kitchen/plugin.rb +76 -0
  47. data/lib/kitchen/plugin_base.rb +60 -0
  48. data/lib/kitchen/provisioner/base.rb +269 -0
  49. data/lib/kitchen/provisioner/chef/berkshelf.rb +116 -0
  50. data/lib/kitchen/provisioner/chef/common_sandbox.rb +350 -0
  51. data/lib/kitchen/provisioner/chef/policyfile.rb +163 -0
  52. data/lib/kitchen/provisioner/chef_apply.rb +121 -0
  53. data/lib/kitchen/provisioner/chef_base.rb +705 -0
  54. data/lib/kitchen/provisioner/chef_infra.rb +167 -0
  55. data/lib/kitchen/provisioner/chef_solo.rb +82 -0
  56. data/lib/kitchen/provisioner/chef_zero.rb +12 -0
  57. data/lib/kitchen/provisioner/dummy.rb +75 -0
  58. data/lib/kitchen/provisioner/shell.rb +157 -0
  59. data/lib/kitchen/provisioner.rb +42 -0
  60. data/lib/kitchen/rake_tasks.rb +80 -0
  61. data/lib/kitchen/shell_out.rb +90 -0
  62. data/lib/kitchen/ssh.rb +289 -0
  63. data/lib/kitchen/state_file.rb +112 -0
  64. data/lib/kitchen/suite.rb +48 -0
  65. data/lib/kitchen/thor_tasks.rb +63 -0
  66. data/lib/kitchen/transport/base.rb +236 -0
  67. data/lib/kitchen/transport/dummy.rb +78 -0
  68. data/lib/kitchen/transport/exec.rb +145 -0
  69. data/lib/kitchen/transport/ssh.rb +579 -0
  70. data/lib/kitchen/transport/winrm.rb +546 -0
  71. data/lib/kitchen/transport.rb +40 -0
  72. data/lib/kitchen/util.rb +229 -0
  73. data/lib/kitchen/verifier/base.rb +243 -0
  74. data/lib/kitchen/verifier/busser.rb +275 -0
  75. data/lib/kitchen/verifier/dummy.rb +75 -0
  76. data/lib/kitchen/verifier/shell.rb +99 -0
  77. data/lib/kitchen/verifier.rb +39 -0
  78. data/lib/kitchen/version.rb +20 -0
  79. data/lib/kitchen/which.rb +26 -0
  80. data/lib/kitchen.rb +152 -0
  81. data/lib/vendor/hash_recursive_merge.rb +79 -0
  82. data/support/busser_install_command.ps1 +14 -0
  83. data/support/busser_install_command.sh +21 -0
  84. data/support/chef-client-fail-if-update-handler.rb +15 -0
  85. data/support/chef_base_init_command.ps1 +18 -0
  86. data/support/chef_base_init_command.sh +1 -0
  87. data/support/chef_base_install_command.ps1 +85 -0
  88. data/support/chef_base_install_command.sh +229 -0
  89. data/support/download_helpers.sh +109 -0
  90. data/support/dummy-validation.pem +27 -0
  91. data/templates/driver/CHANGELOG.md.erb +3 -0
  92. data/templates/driver/Gemfile.erb +3 -0
  93. data/templates/driver/README.md.erb +64 -0
  94. data/templates/driver/Rakefile.erb +21 -0
  95. data/templates/driver/driver.rb.erb +23 -0
  96. data/templates/driver/gemspec.erb +29 -0
  97. data/templates/driver/gitignore.erb +17 -0
  98. data/templates/driver/license_apachev2.erb +15 -0
  99. data/templates/driver/license_lgplv3.erb +16 -0
  100. data/templates/driver/license_mit.erb +22 -0
  101. data/templates/driver/license_reserved.erb +5 -0
  102. data/templates/driver/tailor.erb +4 -0
  103. data/templates/driver/travis.yml.erb +11 -0
  104. data/templates/driver/version.rb.erb +12 -0
  105. data/templates/init/chefignore.erb +2 -0
  106. data/templates/init/kitchen.yml.erb +18 -0
  107. data/test-kitchen.gemspec +52 -0
  108. metadata +528 -0
@@ -0,0 +1,229 @@
1
+ #
2
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
+ #
4
+ # Copyright (C) 2012, 2013, 2014, Fletcher Nichol
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require_relative "errors"
19
+ require "thor/util"
20
+
21
+ module Kitchen
22
+ # Stateless utility methods used in different contexts. Essentially a mini
23
+ # PassiveSupport library.
24
+ #
25
+ # @author Fletcher Nichol <fnichol@nichol.ca>
26
+ module Util
27
+ # Returns the standard library Logger level constants for a given symbol
28
+ # representation.
29
+ #
30
+ # @param symbol [Symbol] symbol representation of a logger level (:debug,
31
+ # :info, :warn, :error, :fatal)
32
+ # @return [Integer] Logger::Severity constant value or nil if input is not
33
+ # valid
34
+ def self.to_logger_level(symbol)
35
+ return nil unless %i{debug info warn error fatal}.include?(symbol)
36
+
37
+ Logger.const_get(symbol.to_s.upcase)
38
+ end
39
+
40
+ # Returns the symbol represenation of a logging levels for a given
41
+ # standard library Logger::Severity constant.
42
+ #
43
+ # @param const [Integer] Logger::Severity constant value for a logging
44
+ # level (Logger::DEBUG, Logger::INFO, Logger::WARN, Logger::ERROR,
45
+ # Logger::FATAL)
46
+ # @return [Symbol] symbol representation of the logging level
47
+ def self.from_logger_level(const)
48
+ case const
49
+ when Logger::DEBUG then :debug
50
+ when Logger::INFO then :info
51
+ when Logger::WARN then :warn
52
+ when Logger::ERROR then :error
53
+ else :fatal
54
+ end
55
+ end
56
+
57
+ # Returns a new Hash with all key values coerced to symbols. All keys
58
+ # within a Hash are coerced by calling #to_sym and hashes within arrays
59
+ # and other hashes are traversed.
60
+ #
61
+ # @param obj [Object] the hash to be processed. While intended for
62
+ # hashes, this method safely processes arbitrary objects
63
+ # @return [Object] a converted hash with all keys as symbols
64
+ def self.symbolized_hash(obj)
65
+ if obj.is_a?(Hash)
66
+ obj.inject({}) { |h, (k, v)| h[k.to_sym] = symbolized_hash(v); h }
67
+ elsif obj.is_a?(Array)
68
+ obj.inject([]) { |a, e| a << symbolized_hash(e); a }
69
+ else
70
+ obj
71
+ end
72
+ end
73
+
74
+ # Returns a new Hash with all key values coerced to strings. All keys
75
+ # within a Hash are coerced by calling #to_s and hashes with arrays
76
+ # and other hashes are traversed.
77
+ #
78
+ # @param obj [Object] the hash to be processed. While intended for
79
+ # hashes, this method safely processes arbitrary objects
80
+ # @return [Object] a converted hash with all keys as strings
81
+ def self.stringified_hash(obj)
82
+ if obj.is_a?(Hash)
83
+ obj.inject({}) { |h, (k, v)| h[k.to_s] = stringified_hash(v); h }
84
+ elsif obj.is_a?(Array)
85
+ obj.inject([]) { |a, e| a << stringified_hash(e); a }
86
+ else
87
+ obj
88
+ end
89
+ end
90
+
91
+ # Returns a formatted string representing a duration in seconds.
92
+ #
93
+ # @param total [Integer] the total number of seconds
94
+ # @return [String] a formatted string of the form (XmYY.00s)
95
+ def self.duration(total)
96
+ total = 0 if total.nil?
97
+ minutes = (total / 60).to_i
98
+ seconds = (total - (minutes * 60))
99
+ format("(%dm%.2fs)", minutes, seconds)
100
+ end
101
+
102
+ # Generates a command (or series of commands) wrapped so that it can be
103
+ # invoked on a remote instance or locally.
104
+ #
105
+ # This method uses the Bourne shell (/bin/sh) to maximize the chance of
106
+ # cross platform portability on Unixlike systems.
107
+ #
108
+ # @param [String] the command
109
+ # @return [String] a wrapped command string
110
+ def self.wrap_command(cmd)
111
+ cmd = "false" if cmd.nil?
112
+ cmd = "true" if cmd.to_s.empty?
113
+ cmd = cmd.sub(/\n\Z/, "") if /\n\Z/.match?(cmd)
114
+
115
+ "sh -c '\n#{cmd}\n'"
116
+ end
117
+
118
+ # Modifes the given string to strip leading whitespace on each line, the
119
+ # amount which is calculated by using the first line of text.
120
+ #
121
+ # @example
122
+ #
123
+ # string = <<-STRING
124
+ # a
125
+ # b
126
+ # c
127
+ # STRING
128
+ # Util.outdent!(string) # => "a\n b\nc\n"
129
+ #
130
+ # @param string [String] the string that will be modified
131
+ # @return [String] the modified string
132
+ def self.outdent!(string)
133
+ string.gsub!(/^ {#{string.index(/[^ ]/)}}/, "")
134
+ end
135
+
136
+ # Returns a set of Bourne Shell (AKA /bin/sh) compatible helper
137
+ # functions. This function is usually called inline in a string that
138
+ # will be executed remotely on a test instance.
139
+ #
140
+ # @return [String] a string representation of useful helper functions
141
+ def self.shell_helpers
142
+ IO.read(File.join(
143
+ File.dirname(__FILE__), %w{.. .. support download_helpers.sh}
144
+ ))
145
+ end
146
+
147
+ # Lists the contents of the given directory. path will be prepended
148
+ # to the list returned. '.' and '..' are never returned.
149
+ #
150
+ # @param path [String] the directory to list
151
+ # @param include_dot [Boolean] if true, dot files will be included
152
+ # @param recurse [Boolean] if true, listing will be recursive
153
+ # @return A listing of the specified path
154
+ #
155
+ # @note You should prefer this method to using Dir.glob directly. The reason is
156
+ # because Dir.glob behaves strangely on Windows. It wont accept '\'
157
+ # and doesn't like fake directories (C:\Documents and Settings)
158
+ # It also does not do any sort of error checking, so things one would
159
+ # expect to fail just return an empty list
160
+ #
161
+ # @note Dir.chdir is applied to the process, thus it is not thread-safe
162
+ # and must be synchronized.
163
+ def self.list_directory(path, include_dot: false, recurse: false)
164
+ # Things (such as tests) are relying on this to not blow up if
165
+ # the directory does not exist
166
+ return [] unless Dir.exist?(path)
167
+
168
+ Kitchen.mutex_chdir.synchronize do
169
+ Dir.chdir(path) do
170
+ glob_pattern = if recurse
171
+ "**/*"
172
+ else
173
+ "*"
174
+ end
175
+ flags = if include_dot
176
+ [File::FNM_DOTMATCH]
177
+ else
178
+ []
179
+ end
180
+ Dir.glob(glob_pattern, *flags)
181
+ .reject { |f| [".", ".."].include?(f) }
182
+ .map { |f| File.join(path, f) }
183
+ end
184
+ end
185
+ end
186
+
187
+ # Similar to Dir.glob.
188
+ #
189
+ # The difference is this function forces you to specify where to glob from.
190
+ # You should glob from the path closest to what you want. The reason for this
191
+ # is because if you have symlinks on windows of any kind, Dir.glob will not
192
+ # traverse them.
193
+ #
194
+ # @param path [String] the directory to glob from
195
+ # @param pattern [String] The pattern to match
196
+ # @param flags [Integer] You can specify flags you would have passed to Dir.glob
197
+ # @return Files matching the specified pattern in the given path
198
+ #
199
+ # @note Dir.chdir is applied to the process, thus it is not thread-safe
200
+ # and must be synchronized.
201
+ def self.safe_glob(path, pattern, *flags)
202
+ return [] unless Dir.exist?(path)
203
+
204
+ Kitchen.mutex_chdir.synchronize do
205
+ Dir.chdir(path) do
206
+ Dir.glob(pattern, *flags).map { |f| File.join(path, f) }
207
+ end
208
+ end
209
+ end
210
+
211
+ def self.camel_case(a_string)
212
+ Thor::Util.camel_case(a_string)
213
+ end
214
+
215
+ def self.snake_case(a_string)
216
+ Thor::Util.snake_case(a_string)
217
+ end
218
+
219
+ # Check if a cmd exists on the PATH
220
+ def self.command_exists?(cmd)
221
+ paths = ENV["PATH"].split(File::PATH_SEPARATOR) + [ "/bin", "/usr/bin", "/sbin", "/usr/sbin" ]
222
+ paths.each do |path|
223
+ filename = File.join(path, cmd)
224
+ return filename if File.executable?(filename)
225
+ end
226
+ false
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,243 @@
1
+ #
2
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
+ #
4
+ # Copyright (C) 2015, Fletcher Nichol
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require_relative "../errors"
19
+ require_relative "../configurable"
20
+ require_relative "../logging"
21
+ require_relative "../plugin_base"
22
+
23
+ module Kitchen
24
+ module Verifier
25
+ # Base class for a verifier.
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
28
+ class Base < Kitchen::Plugin::Base
29
+ include Configurable
30
+ include Logging
31
+
32
+ default_config :http_proxy, nil
33
+ default_config :https_proxy, nil
34
+ default_config :ftp_proxy, nil
35
+
36
+ default_config :root_path do |verifier|
37
+ verifier.windows_os? ? '$env:TEMP\\verifier' : "/tmp/verifier"
38
+ end
39
+
40
+ default_config :sudo do |verifier|
41
+ verifier.windows_os? ? nil : true
42
+ end
43
+
44
+ default_config :chef_omnibus_root, "/opt/chef"
45
+
46
+ default_config :sudo_command do |verifier|
47
+ verifier.windows_os? ? nil : "sudo -E"
48
+ end
49
+
50
+ default_config :command_prefix, nil
51
+
52
+ default_config(:suite_name) { |busser| busser.instance.suite.name }
53
+
54
+ # Creates a new Verifier object using the provided configuration data
55
+ # which will be merged with any default configuration.
56
+ #
57
+ # @param config [Hash] provided verifier configuration
58
+ def initialize(config = {})
59
+ init_config(config)
60
+ end
61
+
62
+ # Runs the verifier on the instance.
63
+ #
64
+ # @param state [Hash] mutable instance state
65
+ # @raise [ActionFailed] if the action could not be completed
66
+ def call(state)
67
+ create_sandbox
68
+ sandbox_dirs = Util.list_directory(sandbox_path)
69
+
70
+ instance.transport.connection(state) do |conn|
71
+ conn.execute(install_command)
72
+ conn.execute(init_command)
73
+ info("Transferring files to #{instance.to_str}")
74
+ conn.upload(sandbox_dirs, config[:root_path])
75
+ debug("Transfer complete")
76
+ conn.execute(prepare_command)
77
+ conn.execute(run_command)
78
+
79
+ info("Downloading files from #{instance.to_str}")
80
+ config[:downloads].to_h.each do |remotes, local|
81
+ debug("Downloading #{Array(remotes).join(", ")} to #{local}")
82
+ conn.download(remotes, local)
83
+ end
84
+ debug("Download complete")
85
+ end
86
+ rescue Kitchen::Transport::TransportFailed => ex
87
+ raise ActionFailed, ex.message
88
+ ensure
89
+ cleanup_sandbox
90
+ end
91
+
92
+ # Check system and configuration for common errors.
93
+ #
94
+ # @param state [Hash] mutable instance state
95
+ # @returns [Boolean] Return true if a problem is found.
96
+ def doctor(state)
97
+ false
98
+ end
99
+
100
+ # Deletes the sandbox path. Without calling this method, the sandbox path
101
+ # will persist after the process terminates. In other words, cleanup is
102
+ # explicit. This method is safe to call multiple times.
103
+ def cleanup_sandbox
104
+ return if sandbox_path.nil?
105
+
106
+ debug("Cleaning up local sandbox in #{sandbox_path}")
107
+ FileUtils.rmtree(sandbox_path)
108
+ end
109
+
110
+ # Creates a temporary directory on the local workstation into which
111
+ # verifier related files and directories can be copied or created. The
112
+ # contents of this directory will be copied over to the instance before
113
+ # invoking the verifier's run command. After this method completes, it
114
+ # is expected that the contents of the sandbox is complete and ready for
115
+ # copy to the remote instance.
116
+ #
117
+ # **Note:** any subclasses would be well advised to call super first when
118
+ # overriding this method, for example:
119
+ #
120
+ # @example overriding `#create_sandbox`
121
+ #
122
+ # class MyVerifier < Kitchen::Verifier::Base
123
+ # def create_sandbox
124
+ # super
125
+ # # any further file copies, preparations, etc.
126
+ # end
127
+ # end
128
+ def create_sandbox
129
+ @sandbox_path = Dir.mktmpdir("#{instance.name}-sandbox-")
130
+ File.chmod(0755, sandbox_path)
131
+ info("Preparing files for transfer")
132
+ debug("Creating local sandbox in #{sandbox_path}")
133
+ end
134
+
135
+ # Generates a command string which will install and configure the
136
+ # verifier software on an instance. If no work is required, then `nil`
137
+ # will be returned.
138
+ #
139
+ # @return [String] a command string
140
+ def install_command; end
141
+
142
+ # Generates a command string which will perform any data initialization
143
+ # or configuration required after the verifier software is installed
144
+ # but before the sandbox has been transferred to the instance. If no work
145
+ # is required, then `nil` will be returned.
146
+ #
147
+ # @return [String] a command string
148
+ def init_command; end
149
+
150
+ # Generates a command string which will perform any commands or
151
+ # configuration required just before the main verifier run command but
152
+ # after the sandbox has been transferred to the instance. If no work is
153
+ # required, then `nil` will be returned.
154
+ #
155
+ # @return [String] a command string
156
+ def prepare_command; end
157
+
158
+ # Generates a command string which will invoke the main verifier
159
+ # command on the prepared instance. If no work is required, then `nil`
160
+ # will be returned.
161
+ #
162
+ # @return [String] a command string
163
+ def run_command; end
164
+
165
+ # Returns the absolute path to the sandbox directory or raises an
166
+ # exception if `#create_sandbox` has not yet been called.
167
+ #
168
+ # @return [String] the absolute path to the sandbox directory
169
+ # @raise [ClientError] if the sandbox directory has no yet been created
170
+ # by calling `#create_sandbox`
171
+ def sandbox_path
172
+ @sandbox_path ||= raise ClientError, "Sandbox directory has not yet " \
173
+ "been created. Please run #{self.class}#create_sandox before " \
174
+ "trying to access the path."
175
+ end
176
+
177
+ # Sets the API version for this verifier. If the verifier does not set
178
+ # this value, then `nil` will be used and reported.
179
+ #
180
+ # Sets the API version for this verifier
181
+ #
182
+ # @example setting an API version
183
+ #
184
+ # module Kitchen
185
+ # module Verifier
186
+ # class NewVerifier < Kitchen::Verifier::Base
187
+ #
188
+ # kitchen_verifier_api_version 2
189
+ #
190
+ # end
191
+ # end
192
+ # end
193
+ #
194
+ # @param version [Integer,String] a version number
195
+ #
196
+ def self.kitchen_verifier_api_version(version)
197
+ @api_version = version
198
+ end
199
+
200
+ private
201
+
202
+ # Builds a complete command given a variables String preamble and a file
203
+ # containing shell code.
204
+ #
205
+ # @param vars [String] shell variables, as a String
206
+ # @param file [String] file basename (without extension) containing
207
+ # shell code
208
+ # @return [String] command
209
+ # @api private
210
+ def shell_code_from_file(vars, file)
211
+ src_file = File.join(
212
+ File.dirname(__FILE__),
213
+ %w{.. .. .. support},
214
+ file + (powershell_shell? ? ".ps1" : ".sh")
215
+ )
216
+
217
+ wrap_shell_code([vars, "", IO.read(src_file)].join("\n"))
218
+ end
219
+
220
+ # Conditionally prefixes a command with a sudo command.
221
+ #
222
+ # @param command [String] command to be prefixed
223
+ # @return [String] the command, conditionaly prefixed with sudo
224
+ # @api private
225
+ def sudo(script)
226
+ config[:sudo] ? "#{config[:sudo_command]} #{script}" : script
227
+ end
228
+
229
+ # Conditionally prefixes a command with a command prefix.
230
+ # This should generally be done after a command has been
231
+ # conditionally prefixed by #sudo as certain platforms, such as
232
+ # Cisco Nexus, require all commands to be run with a prefix to
233
+ # obtain outbound network access.
234
+ #
235
+ # @param command [String] command to be prefixed
236
+ # @return [String] the command, conditionally prefixed with the configured prefix
237
+ # @api private
238
+ def prefix_command(script)
239
+ config[:command_prefix] ? "#{config[:command_prefix]} #{script}" : script
240
+ end
241
+ end
242
+ end
243
+ end