kitchen-ssh 0.0.5 → 0.0.6

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.
data/README.md CHANGED
@@ -12,6 +12,10 @@ specify driver parameters
12
12
  * ssh_key
13
13
  * forward_agent
14
14
 
15
+ There is also a second driver called ssh_gzip that will also gzip file before transfer which can provide
16
+ a big performance improvement when alot of files are transfered.
17
+
18
+
15
19
  ## Installation
16
20
 
17
21
  Add this line to your application's Gemfile:
@@ -28,5 +32,5 @@ Or install it yourself as:
28
32
 
29
33
  ## Usage
30
34
 
31
- In your .kitchen.yml file set driver to be 'ssh'
35
+ In your .kitchen.yml file set driver to be 'ssh' or 'ssh_gzip'.
32
36
 
data/kitchen-ssh.gemspec CHANGED
@@ -9,6 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Neill Turner"]
10
10
  s.email = ["neillwturner@gmail.com"]
11
11
  s.homepage = "https://github.com/neillturner/kitchen-ssh"
12
+ s.add_dependency('minitar', '~> 0.5')
12
13
  s.summary = "ssh driver for test-kitchen for any running server with an ip address"
13
14
  candidates = Dir.glob("{lib}/**/*") + ['README.md', 'LICENSE.txt', 'kitchen-ssh.gemspec']
14
15
  s.files = candidates.sort
@@ -0,0 +1,251 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2012, 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 'archive/tar/minitar'
20
+
21
+ module Kitchen
22
+
23
+ module Driver
24
+
25
+ # Base class for a driver that uses SSH to communication with an instance.
26
+ # A subclass must implement the following methods:
27
+ # * #create(state)
28
+ # * #destroy(state)
29
+ #
30
+ # @author Fletcher Nichol <fnichol@nichol.ca>
31
+ class SSHBaseGzip < Base
32
+
33
+ default_config :sudo, true
34
+ default_config :port, 22
35
+ default_config :sandbox_archive, 'testkitchen-sandbox.tar.gz'
36
+
37
+ # (see Base#create)
38
+ def create(state) # rubocop:disable Lint/UnusedMethodArgument
39
+ raise ClientError, "#{self.class}#create must be implemented"
40
+ end
41
+
42
+ # (see Base#converge)
43
+ def converge(state)
44
+ provisioner = instance.provisioner
45
+ provisioner.create_sandbox
46
+
47
+ Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
48
+ run_remote(provisioner.install_command, conn)
49
+ run_remote(provisioner.init_command, conn)
50
+ do_sandbox_transfer provisioner, conn
51
+ run_remote(provisioner.prepare_command, conn)
52
+ run_remote(provisioner.run_command, conn)
53
+ end
54
+ ensure
55
+ provisioner && provisioner.cleanup_sandbox
56
+ end
57
+
58
+ # (see Base#setup)
59
+ def setup(state)
60
+ Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
61
+ run_remote(busser.setup_cmd, conn)
62
+ end
63
+ end
64
+
65
+ # (see Base#verify)
66
+ def verify(state)
67
+ Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
68
+ run_remote(busser.sync_cmd, conn)
69
+ run_remote(busser.run_cmd, conn)
70
+ end
71
+ end
72
+
73
+ # (see Base#destroy)
74
+ def destroy(state) # rubocop:disable Lint/UnusedMethodArgument
75
+ raise ClientError, "#{self.class}#destroy must be implemented"
76
+ end
77
+
78
+ # (see Base#login_command)
79
+ def login_command(state)
80
+ SSH.new(*build_ssh_args(state)).login_command
81
+ end
82
+
83
+ # Executes an arbitrary command on an instance over an SSH connection.
84
+ #
85
+ # @param state [Hash] mutable instance and driver state
86
+ # @param command [String] the command to be executed
87
+ # @raise [ActionFailed] if the command could not be successfully completed
88
+ def remote_command(state, command)
89
+ Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
90
+ run_remote(command, conn)
91
+ end
92
+ end
93
+
94
+ # **(Deprecated)** Executes a remote command over SSH.
95
+ #
96
+ # @param ssh_args [Array] ssh arguments
97
+ # @param command [String] remote command to invoke
98
+ # @deprecated This method should no longer be called directly and exists
99
+ # to support very old drivers. This will be removed in the future.
100
+ def ssh(ssh_args, command)
101
+ Kitchen::SSH.new(*ssh_args) do |conn|
102
+ run_remote(command, conn)
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ # Builds arguments for constructing a `Kitchen::SSH` instance.
109
+ #
110
+ # @param state [Hash] state hash
111
+ # @return [Array] SSH constructor arguments
112
+ # @api private
113
+ def build_ssh_args(state)
114
+ combined = config.to_hash.merge(state)
115
+
116
+ opts = Hash.new
117
+ opts[:user_known_hosts_file] = "/dev/null"
118
+ opts[:paranoid] = false
119
+ opts[:keys_only] = true if combined[:ssh_key]
120
+ opts[:password] = combined[:password] if combined[:password]
121
+ opts[:forward_agent] = combined[:forward_agent] if combined.key? :forward_agent
122
+ opts[:port] = combined[:port] if combined[:port]
123
+ opts[:keys] = Array(combined[:ssh_key]) if combined[:ssh_key]
124
+ opts[:logger] = logger
125
+
126
+ [combined[:hostname], combined[:username], opts]
127
+ end
128
+
129
+ # Adds http and https proxy environment variables to a command, if set
130
+ # in configuration data.
131
+ #
132
+ # @param cmd [String] command string
133
+ # @return [String] command string
134
+ # @api private
135
+ def env_cmd(cmd)
136
+ env = "env"
137
+ env << " http_proxy=#{config[:http_proxy]}" if config[:http_proxy]
138
+ env << " https_proxy=#{config[:https_proxy]}" if config[:https_proxy]
139
+
140
+ env == "env" ? cmd : "#{env} #{cmd}"
141
+ end
142
+
143
+ # Executes a remote command over SSH.
144
+ #
145
+ # @param command [String] remove command to run
146
+ # @param connection [Kitchen::SSH] an SSH connection
147
+ # @raise [ActionFailed] if an exception occurs
148
+ # @api private
149
+ def run_remote(command, connection)
150
+ return if command.nil?
151
+
152
+ connection.exec(env_cmd(command))
153
+ rescue SSHFailed, Net::SSH::Exception => ex
154
+ raise ActionFailed, ex.message
155
+ end
156
+
157
+ # Transfers one or more local paths over SSH.
158
+ #
159
+ # @param locals [Array<String>] array of local paths
160
+ # @param remote [String] remote destination path
161
+ # @param connection [Kitchen::SSH] an SSH connection
162
+ # @raise [ActionFailed] if an exception occurs
163
+ # @api private
164
+ def transfer_path(locals, remote, connection)
165
+ return if locals.nil? || Array(locals).empty?
166
+
167
+ info("Transferring files to #{instance.to_str}")
168
+ locals.each { |local| connection.upload_path!(local, remote) }
169
+ debug("Transfer complete")
170
+ rescue SSHFailed, Net::SSH::Exception => ex
171
+ raise ActionFailed, ex.message
172
+ end
173
+
174
+ # Blocks until a TCP socket is available where a remote SSH server
175
+ # should be listening.
176
+ #
177
+ # @param hostname [String] remote SSH server host
178
+ # @param username [String] SSH username (default: `nil`)
179
+ # @param options [Hash] configuration hash (default: `{}`)
180
+ # @api private
181
+ def wait_for_sshd(hostname, username = nil, options = {})
182
+ SSH.new(hostname, username, { :logger => logger }.merge(options)).wait
183
+ end
184
+
185
+
186
+ # Creates a temporary folder containing an archive of the current
187
+ # TestKitchen sandbox.
188
+ #
189
+ # @param sandbox_path [String]
190
+ def archive_sandbox(sandbox_path)
191
+ archive_dir = Dir.mktmpdir("#{instance.name}-sandbox-archive-")
192
+ archive_file = "#{archive_dir}/#{self[:sandbox_archive]}"
193
+
194
+ Dir.chdir(sandbox_path) do |dir|
195
+ tgz = Zlib::GzipWriter.new(File.open(archive_file, 'wb'), Zlib::DEFAULT_COMPRESSION, Zlib::DEFAULT_STRATEGY)
196
+ Archive::Tar::Minitar.pack('.', tgz)
197
+ end
198
+
199
+ archive_dir
200
+ end
201
+
202
+ # Transfers the local sandbox to the instance.
203
+ # - Archives/extracts if the tar command is available remotely.
204
+ #
205
+ # @param provisioner [Kitchen::Provisioner::Base] the provisioner
206
+ # @param connection [Kitchen:SSH] an SSH connection
207
+ def do_sandbox_transfer(provisioner, connection)
208
+ root_path = provisioner[:root_path]
209
+ sandbox_path = provisioner.sandbox_path
210
+ archive_file = self[:sandbox_archive]
211
+ archive_path = false
212
+ do_archive = remote_supports_tar? connection
213
+
214
+ begin
215
+ # Archive sandbox if enabled (We keep a copy of the archive path so that we do not)
216
+ # delete the sandbox if an exception is thrown
217
+ if do_archive
218
+ info 'Creating sandbox archive'
219
+ archive_path = archive_sandbox sandbox_path
220
+ sandbox_path = archive_path
221
+ end
222
+
223
+ # Initiate transfer
224
+ transfer_path(Dir.glob("#{sandbox_path}/*"), root_path, connection)
225
+
226
+ # Extract archive if enabled (and cleanup locally)
227
+ if do_archive
228
+ info 'Extracting sandbox archive remotely'
229
+ run_remote("tar xf #{root_path}/#{archive_file} -C #{root_path}", connection)
230
+ end
231
+ ensure
232
+ # Ensure archive temporary directory is removed, if used.
233
+ FileUtils.rmtree(archive_path) if archive_path
234
+ end
235
+ end
236
+
237
+ # Checks whether the remote instance supports archive extraction using
238
+ # the `tar` command.
239
+ #
240
+ # @param connection [Kitchen::SSH] an SSH connection
241
+ def remote_supports_tar?(connection)
242
+ begin
243
+ run_remote('tar --version > /dev/null 2>&1', connection)
244
+ return true
245
+ rescue ActionFailed => ex
246
+ return false
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,30 @@
1
+ require 'kitchen'
2
+ require 'kitchen/driver/ssh_base_gzip'
3
+
4
+ module Kitchen
5
+ module Driver
6
+ class SshGzip < SSHBaseGzip
7
+ def create(state)
8
+ state[:sudo] = config[:sudo]
9
+ state[:port] = config[:port]
10
+ state[:ssh_key] = config[:ssh_key]
11
+ state[:forward_agent] = config[:forward_agent]
12
+ state[:username] = config[:username]
13
+ state[:hostname] = config[:hostname]
14
+ state[:password] = config[:password]
15
+ print "Kitchen-sshGzip does not start your server '#{state[:hostname]}' but will look for an ssh connection with user '#{state[:username]}'"
16
+ wait_for_sshd(state[:hostname], state[:username], {:port => state[:port]})
17
+ print "Kitchen-sshGzip found ssh ready on host '#{state[:hostname]}' with user '#{state[:username]}'\n"
18
+ debug("ssh:create '#{state[:hostname]}'")
19
+ end
20
+
21
+ def destroy(state)
22
+ print "Kitchen-sshGzip does not destroy your server '#{state[:hostname]}' by shutting it down..."
23
+ print "Shutdown your server '#{state[:hostname]}' natively with user '#{state[:username]}'"
24
+ print 'in your cloud or virtualisation console etc.\n'
25
+ debug("ssh:destroy '#{state[:hostname]}'")
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  module Kitchen
2
2
  module Ssh
3
- VERSION = "0.0.5"
3
+ VERSION = "0.0.6"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-ssh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-10-09 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2015-01-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitar
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.5'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '0.5'
14
30
  description: ! '== DESCRIPTION:
15
31
 
16
32
 
@@ -34,6 +50,8 @@ files:
34
50
  - README.md
35
51
  - kitchen-ssh.gemspec
36
52
  - lib/kitchen/driver/ssh.rb
53
+ - lib/kitchen/driver/ssh_base_gzip.rb
54
+ - lib/kitchen/driver/ssh_gzip.rb
37
55
  - lib/kitchen/ssh/version.rb
38
56
  homepage: https://github.com/neillturner/kitchen-ssh
39
57
  licenses: []