kitchen-transport-express 1.3.0 → 1.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/kitchen/transport/express/archiver.rb +36 -11
- data/lib/kitchen/transport/express/version.rb +1 -1
- data/lib/kitchen/transport/express_ssh.rb +36 -27
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7399b24ea4426dcb6ea891466818b0a3471b24a6a67eb2163727afb8f9d7e35
|
|
4
|
+
data.tar.gz: fa22be3398901120654fe61679e913f3b714eaaa59f4c0c68bcd44bde1651410
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7b3e1b72d7b6f5117e286166e07092c83606965115650f6cf18949300280b1761e57ff78f43e375ce5785e317803c043b788b13167e6ea210ffec9c13294624c
|
|
7
|
+
data.tar.gz: 76f54ae47956867487ed15bea11dd1b114c5b92c6776da7efc9e44e4f2843837606c6fbf782ca3988fba4d62ffd3f2b5cf68ff74572e1ae0dbac9f8b35f2460e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# kitchen-transport-express CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 1.4.0
|
|
4
|
+
* feat: ♻️ utilize concurrent futures for enhanced parallelism
|
|
5
|
+
* feat: 🔊 enhanced logging
|
|
6
|
+
|
|
7
|
+
## 1.3.1
|
|
8
|
+
* fix: 🐛 ensure directories that only contain dot files get archived
|
|
9
|
+
|
|
3
10
|
## 1.3.0
|
|
4
11
|
* chore: 📝 minor updates to method documentation
|
|
5
12
|
* chore: 🔧 add metadata to gemspec
|
|
@@ -30,39 +30,64 @@ module Kitchen
|
|
|
30
30
|
def archive(path)
|
|
31
31
|
archive_basename = ::File.basename(path) + ".tgz"
|
|
32
32
|
archive_full_name = ::File.join(::File.dirname(path), archive_basename)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
files = all_files(path)
|
|
34
|
+
start_time = Time.now
|
|
35
|
+
create_archive(path, files, archive_full_name)
|
|
36
|
+
Express.log(logger, "create archive #{File.basename(archive_full_name)} (#{files.size} files)", start_time)
|
|
37
37
|
archive_full_name
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# Transfers the archive to the remote host.
|
|
41
|
+
#
|
|
42
|
+
# @param session [Net::SSH::Connection::Session] the SSH session used to connect to the remote host and execute the extract and cleanup commands.
|
|
43
|
+
# @param local [String] the directory in the local sandbox that is being processed.
|
|
44
|
+
# @param remote [String] the remote directory (kitchen_root).
|
|
45
|
+
# @param opts [Hash] the ssh options that came in from the Kitchen instance.
|
|
46
|
+
def scp(session, local, remote, opts = {})
|
|
47
|
+
start_time = Time.now
|
|
48
|
+
session.scp.upload!(local, remote, opts)
|
|
49
|
+
Express.log(logger, "upload #{File.basename(local)} (Thread ID: #{Thread.current.object_id})", start_time)
|
|
50
|
+
end
|
|
51
|
+
|
|
40
52
|
# Extracts the archive on the remote host.
|
|
41
53
|
#
|
|
42
|
-
# @param session [Net::SSH::Connection::Session]
|
|
54
|
+
# @param session [Net::SSH::Connection::Session] the SSH session used to connect to the remote host and execute the extract and cleanup commands.
|
|
55
|
+
# @param local [String] the directory in the local sandbox that is being processed.
|
|
56
|
+
# @param remote [String] the remote directory (kitchen_root).
|
|
43
57
|
def extract(session, local, remote)
|
|
44
58
|
return unless local.match(/.*\.tgz/)
|
|
45
59
|
|
|
60
|
+
start_time = Time.now
|
|
46
61
|
archive_basename = File.basename(local)
|
|
47
|
-
logger.debug("[#{Express::LOG_PREFIX}] Extracting #{::File.join(remote, archive_basename)}")
|
|
48
62
|
session.open_channel do |channel|
|
|
49
63
|
channel.request_pty
|
|
50
64
|
channel.exec("tar -xzf #{::File.join(remote, archive_basename)} -C #{remote} && rm -f #{File.join(remote, archive_basename)}")
|
|
51
65
|
end
|
|
52
66
|
session.loop
|
|
67
|
+
Express.log(logger, "extract #{File.basename(local)} (Thread ID: #{Thread.current.object_id})", start_time)
|
|
53
68
|
end
|
|
54
69
|
|
|
55
70
|
private
|
|
56
71
|
|
|
72
|
+
# Creates a list of all files that are in the directory to be archived.
|
|
73
|
+
#
|
|
74
|
+
# @param path [String] the path to the directory that will be archived.
|
|
75
|
+
# @return [Array] an array of all files to be archived.
|
|
76
|
+
# @api private
|
|
77
|
+
def all_files(path)
|
|
78
|
+
Dir.glob(File.join(path, "/**/*"), File::FNM_DOTMATCH).reject { |f| %w{. ..}.include? File.basename(f) }
|
|
79
|
+
end
|
|
80
|
+
|
|
57
81
|
# Creats a archive of the directory provided.
|
|
58
82
|
#
|
|
59
83
|
# @param path [String] the path to the directory that will be archived.
|
|
84
|
+
# @param files [Array] the array of all files that will be archived.
|
|
60
85
|
# @param archive_path [String] the fully qualified path to the archive that will be created.
|
|
61
86
|
# @api private
|
|
62
|
-
def create_archive(path, archive_path)
|
|
87
|
+
def create_archive(path, files, archive_path)
|
|
63
88
|
Archive.write_open_filename(archive_path, Archive::COMPRESSION_GZIP,
|
|
64
89
|
Archive::FORMAT_TAR_PAX_RESTRICTED) do |tar|
|
|
65
|
-
write_content(tar, path)
|
|
90
|
+
write_content(tar, path, files)
|
|
66
91
|
end
|
|
67
92
|
end
|
|
68
93
|
|
|
@@ -70,10 +95,10 @@ module Kitchen
|
|
|
70
95
|
#
|
|
71
96
|
# @param tar [Archive::Writer] the instance of the archive class.
|
|
72
97
|
# @param path [String] the path to the directory that will be archived.
|
|
98
|
+
# @param files [Array] the array of all files that will be archived.
|
|
73
99
|
# @api private
|
|
74
|
-
def write_content(tar, path)
|
|
75
|
-
|
|
76
|
-
all_files.each do |f|
|
|
100
|
+
def write_content(tar, path, files)
|
|
101
|
+
files.each do |f|
|
|
77
102
|
if File.file? f
|
|
78
103
|
tar.new_entry do |e|
|
|
79
104
|
entry(e, f, path)
|
|
@@ -27,6 +27,16 @@ module Kitchen
|
|
|
27
27
|
class Express
|
|
28
28
|
# A constant that gets prepended to debugger messages.
|
|
29
29
|
LOG_PREFIX = "EXPRESS"
|
|
30
|
+
|
|
31
|
+
# Logger class method to unify logging.
|
|
32
|
+
#
|
|
33
|
+
# @param logger [Kitchen::Logger] the logger that was created by the kitchen instance.
|
|
34
|
+
# @param message [String] the message to output.
|
|
35
|
+
# @param start_time [Time] the start time of the process if duration is desired to be part of the message.
|
|
36
|
+
def self.log(logger, message = nil, start_time = nil)
|
|
37
|
+
message = "#{message} (#{Time.now - start_time}s)" if start_time
|
|
38
|
+
logger.debug "[#{Express::LOG_PREFIX}] [#{Time.now.getutc.strftime("%Y-%m-%dT%H:%M:%S%:z")}] #{message}"
|
|
39
|
+
end
|
|
30
40
|
end
|
|
31
41
|
|
|
32
42
|
# Express SSH Transport Error class.
|
|
@@ -51,7 +61,7 @@ module Kitchen
|
|
|
51
61
|
# @return [Ssh::Connection] an instance of Kitchen::Transport::ExpressSsh::Connection.
|
|
52
62
|
def create_new_connection(options, &block)
|
|
53
63
|
if @connection
|
|
54
|
-
|
|
64
|
+
Express.log(logger, "shutting previous connection #{@connection}")
|
|
55
65
|
@connection.close
|
|
56
66
|
end
|
|
57
67
|
|
|
@@ -95,33 +105,35 @@ module Kitchen
|
|
|
95
105
|
# @param locals [Array] the top-level list of directories and files to be transfered.
|
|
96
106
|
# @param remote [String] the remote directory (kitchen_root).
|
|
97
107
|
# @raise [ExpressFailed] if any of the threads raised an exception.
|
|
98
|
-
def upload(locals, remote)
|
|
108
|
+
def upload(locals, remote)
|
|
99
109
|
return super unless valid_remote_requirements?(remote)
|
|
100
110
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
pool.wait_for_termination
|
|
112
|
-
|
|
113
|
-
raise ExpressFailed, exceptions.pop unless exceptions.empty?
|
|
114
|
-
end # rubocop: enable Metrics/MethodLength
|
|
111
|
+
start_time = Time.now
|
|
112
|
+
processed_local = process_locals(locals)
|
|
113
|
+
futures = create_futures(processed_local, remote)
|
|
114
|
+
all_done = Concurrent::Promise.zip(*futures).execute
|
|
115
|
+
all_done.value!
|
|
116
|
+
rescue => e
|
|
117
|
+
raise ExpressFailed, e.cause.to_s
|
|
118
|
+
ensure
|
|
119
|
+
Express.log(logger, "transport express complete", start_time)
|
|
120
|
+
end
|
|
115
121
|
|
|
116
122
|
private
|
|
117
123
|
|
|
118
|
-
# Creates the
|
|
124
|
+
# Creates the concurrent futures.
|
|
119
125
|
#
|
|
120
|
-
# @param
|
|
121
|
-
# @return [Array(Concurrent::
|
|
126
|
+
# @param locals [Array] list of files and archives to be uploaded.
|
|
127
|
+
# @return [Array(Concurrent::Promise)]
|
|
122
128
|
# @api private
|
|
123
|
-
def
|
|
124
|
-
|
|
129
|
+
def create_futures(locals, remote)
|
|
130
|
+
# Start upload futures
|
|
131
|
+
executor = Concurrent::FixedThreadPool.new([locals.length, 10].min)
|
|
132
|
+
locals.map do |local|
|
|
133
|
+
Concurrent::Promise.execute(executor: executor) do
|
|
134
|
+
transfer(local, remote, session.options)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
125
137
|
end
|
|
126
138
|
|
|
127
139
|
# Ensures the remote host has the minimum-required executables to extract the archives.
|
|
@@ -134,8 +146,7 @@ module Kitchen
|
|
|
134
146
|
execute("mkdir -p #{remote}")
|
|
135
147
|
true
|
|
136
148
|
rescue => e
|
|
137
|
-
|
|
138
|
-
logger.debug("[#{Express::LOG_PREFIX}] #{e}")
|
|
149
|
+
Express.log(logger, "Requirements not met on remote host for Express transport.\n#{e}")
|
|
139
150
|
false
|
|
140
151
|
end
|
|
141
152
|
|
|
@@ -166,13 +177,11 @@ module Kitchen
|
|
|
166
177
|
# @raise [StandardError] if the files could not be uploaded successfully.
|
|
167
178
|
# @api private
|
|
168
179
|
def transfer(local, remote, opts = {})
|
|
169
|
-
logger.debug("[#{Express::LOG_PREFIX}] Transferring #{local} to #{remote}")
|
|
170
|
-
|
|
171
180
|
Net::SSH.start(session.host, opts[:user], **opts) do |ssh|
|
|
172
|
-
|
|
181
|
+
scp(ssh, local, remote, opts)
|
|
173
182
|
extract(ssh, local, remote)
|
|
174
183
|
rescue Net::SCP::Error => ex
|
|
175
|
-
|
|
184
|
+
Express.log(logger, "upload failed with #{ex.message.strip}")
|
|
176
185
|
raise "(#{ex.message.strip})"
|
|
177
186
|
end
|
|
178
187
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kitchen-transport-express
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Steele
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-08-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: test-kitchen
|
|
@@ -180,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
180
180
|
- !ruby/object:Gem::Version
|
|
181
181
|
version: '0'
|
|
182
182
|
requirements: []
|
|
183
|
-
rubygems_version: 3.3.
|
|
183
|
+
rubygems_version: 3.3.27
|
|
184
184
|
signing_key:
|
|
185
185
|
specification_version: 4
|
|
186
186
|
summary: Skip the long lines in Kitchen Transport!
|