opscode-pushy-client 2.100.0 → 3.0.0.pre

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44b1f6febf06aecd9bffcece1c65126a9e58d9a4fdd1925506782f0400cdf189
4
- data.tar.gz: 8e288e08d58790b64be013bfea14ca6b1838c4e136aa9a19cc1c116a71ccb1ec
3
+ metadata.gz: c77b2f636067d317b9810cc3d5835084e6eb9ce4973af66c40a340caa44e534a
4
+ data.tar.gz: f01c7d95292f4d1d4b6beef2bc695ace5eca8e65b999f0708875b5557950b286
5
5
  SHA512:
6
- metadata.gz: 52ab8e2aa49d1b0af72a1474d798ca711b878fdc1d8f8686c8b3ec9d182770996afd18559af44987a497a3257c4ff38c767881b776d5a830ae664efc8f520801
7
- data.tar.gz: d01ca100485a00b6cdfb8eff100993de90791ef133ac92117d18dca5ac121bbd65a309f1ac9aecf9230340dd023370723b0b3fb1bbb8b618c4178cc0febf32d1
6
+ metadata.gz: bad3691458ecc47aa5167e2f0fa80b039ad318287e0d412f1daf26039a858f78134fe855efcb80bfd0e935eb977ce328a7c31bbcb19142f7013fa36992096fb9
7
+ data.tar.gz: 8f830c8e0af5bfd83e6879cacb7759f95e9835f368421997f1cac034b3587564eb6e38e5cbb63861cf40da6068fcf403711c26467502ef12ed563de83934c907
data/Gemfile CHANGED
@@ -9,6 +9,7 @@ end
9
9
  platforms :mswin, :mingw do
10
10
  gem "ffi"
11
11
  gem "rdp-ruby-wmi"
12
+ gem "windows-api"
12
13
  gem "windows-pr"
13
14
  gem "win32-api"
14
15
  gem "win32-dir"
File without changes
@@ -41,6 +41,7 @@ class PushyClient
41
41
  @file_dir = options[:file_dir] || '/tmp/pushy'
42
42
  @file_dir_expiry = options[:file_dir_expiry] || 86400
43
43
  @allowed_overwritable_env_vars = options[:allowed_overwritable_env_vars]
44
+ @max_body_size = options[:max_body_size] || 63000
44
45
 
45
46
  @allow_unencrypted = options[:allow_unencrypted] || false
46
47
  @client_curve_pub_key, @client_curve_sec_key = ZMQ::Util.curve_keypair
@@ -79,6 +80,7 @@ class PushyClient
79
80
  attr_accessor :node_name
80
81
  attr_accessor :hostname
81
82
  attr_accessor :whitelist
83
+ attr_accessor :max_body_size
82
84
  attr_reader :incarnation_id
83
85
  attr_reader :legacy_mode # indicate we've fallen back to 1.x
84
86
  attr_reader :allowed_overwritable_env_vars
@@ -70,6 +70,12 @@ class PushyClient
70
70
  :description => "The node name for this client",
71
71
  :proc => nil
72
72
 
73
+ option :max_body_size,
74
+ :short => "-M MAX_BODY_SIZE",
75
+ :long => "--max-body-size MAX_BODY_SIZE",
76
+ :description => "MAX_BODY_SIZE of STDER/SDTOUT for this client",
77
+ :proc => nil
78
+
73
79
  option :chef_server_url,
74
80
  :short => "-S CHEFSERVERURL",
75
81
  :long => "--server CHEFSERVERURL",
@@ -133,7 +139,8 @@ class PushyClient
133
139
  :hostname => ohai[:hostname],
134
140
  :filedir => Chef::Config[:file_dir],
135
141
  :allow_unencrypted => Chef::Config[:allow_unencrypted],
136
- :allowed_overwritable_env_vars => Chef::Config[:allowed_overwritable_env_vars]
142
+ :allowed_overwritable_env_vars => Chef::Config[:allowed_overwritable_env_vars],
143
+ :max_body_size => Chef::Config[:max_body_size]
137
144
  )
138
145
 
139
146
  @client.start
@@ -60,14 +60,6 @@ class PushyClient
60
60
  end
61
61
  end
62
62
 
63
- # The maximum size, in bytes, allowed for a message body. This is not
64
- # configurable because it is configurable (though not documented) on the
65
- # server, and we don't want to make users have to sync the two values.
66
- # The max on the server is actually 65536, but we leave a little room since
67
- # the server is measuring the signed message and we're just counting
68
- # the size of the stderr and stdout.
69
- MAX_BODY_SIZE = 63000
70
-
71
63
  def initialize(client)
72
64
  @client = client
73
65
  # We synchronize on this when we change the socket (so if you want a
@@ -114,6 +106,7 @@ class PushyClient
114
106
  @server_public_key = OpenSSL::PKey::RSA.new(client.config['public_key'])
115
107
  @client_private_key = ProtocolHandler::load_key(client.client_key)
116
108
  @max_message_skew = client.config['max_message_skew']
109
+ @max_body_size = client.config['max_body_size']
117
110
 
118
111
  if client.using_curve
119
112
  server_curve_pub_key = client.config['curve_public_key']
@@ -417,9 +410,8 @@ class PushyClient
417
410
  def validate_params(params = {})
418
411
  stdout_bytes = params[:stdout].to_s.bytesize
419
412
  stderr_bytes = params[:stderr].to_s.bytesize
420
-
421
- if (stdout_bytes + stderr_bytes) > MAX_BODY_SIZE
422
- Chef::Log.warn("Command output too long. Will not be sent to server.")
413
+ if (stdout_bytes + stderr_bytes) > client.max_body_size.to_i
414
+ Chef::Log.warn("Command output #{stdout_bytes + stderr_bytes} is larger than the Client MAX_BODY_SIZE of #{client.max_body_size.to_i}")
423
415
  params.delete_if { |k| [:stdout, :stderr].include? k }
424
416
  else
425
417
  params
@@ -471,7 +463,7 @@ class PushyClient
471
463
  @socket_lock.synchronize do
472
464
  @command_socket_outgoing_seq += 1
473
465
  json[:sequence] = @command_socket_outgoing_seq
474
- message = JSON.generate(json)
466
+ message = validate_and_generate_json(json)
475
467
  if @command_socket
476
468
  ProtocolHandler::send_signed_message(@command_socket, method, @client_private_key, @session_key, message, @client)
477
469
  else
@@ -480,6 +472,25 @@ class PushyClient
480
472
  end
481
473
  end
482
474
 
475
+ # This method validates and generates the JSON message
476
+ # params user packet
477
+ # Returns Waring or JSON message
478
+ def validate_and_generate_json(json)
479
+ message = JSON.generate(json)
480
+ if message.to_s.bytesize > client.max_body_size.to_i
481
+ deleted_packet = json.select { |k| [:stdout, :stderr].include? k }
482
+ json.delete_if { |k| [:stdout, :stderr].include? k }
483
+ Chef::Log.warn("Deleted packet #{deleted_packet} from Client packet") if deleted_packet.to_s.size > 1
484
+ # Re-generate json message
485
+ message = JSON.generate(json)
486
+ end
487
+ if message.to_s.bytesize > client.max_body_size.to_i
488
+ Chef::Log.warn("Client message size #{message.to_s.bytesize} is larger than the Client MAX_BODY_SIZE of #{client.max_body_size.to_i}")
489
+ else
490
+ message
491
+ end
492
+ end
493
+
483
494
  def self.send_signed_message(socket, method, client_private_key, session_key, message, client)
484
495
  auth = case method
485
496
  when :rsa2048_sha1
@@ -0,0 +1,308 @@
1
+ # @copyright Copyright 2014 Chef Software, Inc. All Rights Reserved.
2
+ #
3
+ # This file is provided to you under the Apache License,
4
+ # Version 2.0 (the "License"); you may not use this file
5
+ # except in compliance with the License. You may obtain
6
+ # a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing,
11
+ # software distributed under the License is distributed on an
12
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
13
+ # KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations
15
+ # under the License.
16
+ #
17
+
18
+ # This is needed to fix an issue in win32-process v. 0.6.5
19
+ # where Process.wait blocks the entire Ruby interpreter
20
+ # for the duration of the process.
21
+ require 'chef/platform'
22
+ require 'mixlib/shellout'
23
+ if Chef::Platform.windows?
24
+ require 'pushy_client/win32'
25
+ end
26
+
27
+ class PushyClient
28
+ class JobRunner
29
+ def initialize(client)
30
+ @client = client
31
+ @on_job_state_change = []
32
+
33
+ set_job_state(:idle)
34
+ @pid = nil
35
+ @process_thread = nil
36
+
37
+ # Keep job state and process state in sync
38
+ @state_lock = Mutex.new
39
+ end
40
+
41
+ attr_reader :client
42
+ attr_reader :state
43
+ attr_reader :job_id
44
+ attr_reader :command
45
+ attr_reader :lockfile
46
+
47
+ def node_name
48
+ client.node_name
49
+ end
50
+
51
+ def start
52
+ end
53
+
54
+ def stop
55
+ if @state == :running
56
+ kill_process
57
+ end
58
+ set_job_state(:idle)
59
+ end
60
+
61
+ def reconfigure
62
+ # We have no configuration, and keep state between reconfigures
63
+ end
64
+
65
+ def commit(job_id, command, opts)
66
+ @opts = opts
67
+ @state_lock.synchronize do
68
+ if @state == :idle
69
+ # If we're being asked to lock
70
+ if client.whitelist[command] &&
71
+ client.whitelist[command].is_a?(Hash) &&
72
+ client.whitelist[command][:lock]
73
+ # If the command is chef-client
74
+ # We don't want to run if there is already another instance of chef-client going,
75
+ # so we check to see if there is a runlock on chef-client before committing. This
76
+ # currently only works in versions of chef where runlock has been implemented.
77
+
78
+ # The location of our lockfile
79
+ if client.whitelist[command][:lock] == true
80
+ lockfile_location = Chef::Config[:lockfile] || "#{Chef::Config[:file_cache_path]}/chef-client-running.pid"
81
+ else
82
+ lockfile_location = client.whitelist[command][:lock]
83
+ end
84
+ # Open the Lockfile
85
+ begin
86
+ @lockfile = File.open(lockfile_location, 'w')
87
+ locked = lockfile.flock(File::LOCK_EX|File::LOCK_NB)
88
+ unless locked
89
+ Chef::Log.info("[#{node_name}] Received commit #{job_id} but is already running '#{command}'")
90
+ client.send_command(:nack_commit, job_id)
91
+ return false
92
+ end
93
+ rescue Errno::ENOENT
94
+ end
95
+ elsif client.whitelist[command]
96
+ user_ok = check_user(job_id)
97
+ dir_ok = check_dir(job_id)
98
+ file_ok = check_file(job_id)
99
+ if user_ok && dir_ok && file_ok
100
+ Chef::Log.info("[#{node_name}] Received commit #{job_id}")
101
+ set_job_state(:committed, job_id, command)
102
+ client.send_command(:ack_commit, job_id)
103
+ true
104
+ else
105
+ client.send_command(:nack_commit, job_id)
106
+ end
107
+ else
108
+ Chef::Log.error("[#{node_name}] Received commit #{job_id}, but command '#{command}' is not in the whitelist!")
109
+ client.send_command(:nack_commit, job_id)
110
+ false
111
+ end
112
+ else
113
+ Chef::Log.warn("[#{node_name}] Received commit #{job_id} but current state is #{@state} #{@job_id}")
114
+ client.send_command(:nack_commit, job_id)
115
+ false
116
+ end
117
+ end
118
+ end
119
+
120
+ def run(job_id)
121
+ @state_lock.synchronize do
122
+ if @state == :committed && @job_id == job_id
123
+ Chef::Log.info("[#{node_name}] Received run #{job_id}")
124
+ pid, process_thread = start_process
125
+ set_job_state(:running, job_id, @command, pid, process_thread)
126
+ client.send_command(:ack_run, job_id)
127
+ true
128
+ else
129
+ Chef::Log.warn("[#{node_name}] Received run #{job_id} but current state is #{@state} #{@job_id}")
130
+ client.send_command(:nack_run, job_id)
131
+ false
132
+ end
133
+ end
134
+ end
135
+
136
+ def abort
137
+ Chef::Log.info("[#{node_name}] Received abort")
138
+ @state_lock.synchronize do
139
+ _job_id = job_id
140
+ stop
141
+ client.send_command(:aborted, _job_id)
142
+ end
143
+ end
144
+
145
+ def job_state
146
+ @state_lock.synchronize do
147
+ get_job_state
148
+ end
149
+ end
150
+
151
+ def on_job_state_change(&block)
152
+ @on_job_state_change << block
153
+ end
154
+
155
+ private
156
+
157
+ def get_job_state
158
+ {
159
+ :state => @state,
160
+ :job_id => @job_id,
161
+ :command => @command
162
+ }
163
+ end
164
+
165
+ def set_job_state(state, job_id = nil, command = nil, pid = nil, process_thread = nil)
166
+ if state == :idle || state == :running
167
+ if @lockfile
168
+ # If there is a lockfile Release the lock to allow chef-client to run
169
+ lockfile.flock(File::LOCK_UN)
170
+ lockfile.close
171
+ end
172
+ end
173
+ @state = state
174
+ @job_id = job_id
175
+ @command = command
176
+ @pid = pid
177
+ @process_thread = process_thread
178
+
179
+ # Notify people of the change
180
+ @on_job_state_change.each { |block| block.call(get_job_state) }
181
+ end
182
+
183
+ def completed(job_id, exit_code, stdout, stderr)
184
+ Chef::Log.info("[#{node_name}] Job #{job_id} completed with exit code #{exit_code}")
185
+ @state_lock.synchronize do
186
+ if @state == :running && @job_id == job_id
187
+ set_job_state(:idle)
188
+ status = exit_code == 0 ? :succeeded : :failed
189
+ params = {}
190
+ params[:stdout] = stdout if stdout
191
+ params[:stderr] = stderr if stderr
192
+ client.send_command(status, job_id, params)
193
+ end
194
+ end
195
+ end
196
+
197
+ def start_process
198
+ # _pid and _job_id are local variables so that if @pid or @job_id change
199
+ # for any reason (for example, they become nil), the thread we create
200
+ # still tracks the correct pid.
201
+ if client.whitelist[command].is_a?(Hash)
202
+ command_line = client.whitelist[command][:command_line]
203
+ else
204
+ command_line = client.whitelist[command]
205
+ end
206
+ user = @opts['user']
207
+ dir = @opts['dir']
208
+ env = @opts['env'] || {}
209
+ capture = @opts['capture'] || false
210
+ path = extract_file
211
+ env.merge!({'CHEF_PUSH_JOB_FILE' => path}) if path
212
+ std_env = {'CHEF_PUSH_NODE_NAME' => node_name, 'CHEF_PUSH_JOB_ID' => @job_id}
213
+ env.merge!(std_env)
214
+ # XXX We set the timeout to 86400, because the time in ShellOut is
215
+ # 60 seconds, and that might be too slow. But we currently don't
216
+ # have the timeout from the pushy-server. Instead of changing it from
217
+ # a hard-coded value to a config option, we should expand the protocol
218
+ # to support sending the timeout.
219
+ command = Mixlib::ShellOut.new(command_line,
220
+ :user => user,
221
+ :cwd => dir,
222
+ :env => env,
223
+ :timeout => 86400)
224
+ _job_id = @job_id
225
+ # Can't get the _pid from the ShellOut command. So
226
+ # we can't kill it, either.
227
+ _pid = nil
228
+ Chef::Log.info("[#{node_name}] Job #{job_id}: started command '#{command_line}' with PID '#{_pid}'")
229
+
230
+ # Wait for the job to complete and close it out.
231
+ process_thread = Thread.new do
232
+ begin
233
+ command.run_command
234
+ stdout = command.stdout if capture
235
+ stderr = command.stderr if capture
236
+ completed(_job_id, command.status.exitstatus, stdout, stderr)
237
+ rescue
238
+ client.log_exception("Exception raised while waiting for job #{_job_id} to complete", $!)
239
+ abort
240
+ end
241
+ end
242
+
243
+ [ _pid, process_thread ]
244
+ end
245
+
246
+ def kill_process
247
+ Chef::Log.info("[#{node_name}] Killing process #{@pid}")
248
+ @process_thread.kill
249
+ @process_thread.join
250
+ begin
251
+ Process.kill(1, @pid) if @pid
252
+ rescue
253
+ client.log_exception("Exception in Process.kill(1, #{@pid})", $!)
254
+ end
255
+ end
256
+
257
+ def check_user(job_id)
258
+ user = @opts['user']
259
+ if user
260
+ begin
261
+ Etc.getpwnam(user)
262
+ true
263
+ rescue
264
+ Chef::Log.error("[#{node_name}] Received commit #{job_id}, but user '#{user}' does not exist!")
265
+ false
266
+ end
267
+ else
268
+ true
269
+ end
270
+ end
271
+
272
+ def check_dir(job_id)
273
+ # XX Perhaps should be stricted, e.g. forking a process to actually try to chdir
274
+ dir = @opts['dir']
275
+ dir_ok = !dir || Dir.exists?(dir)
276
+ Chef::Log.error("[#{node_name}] Received commit #{job_id}, but dir '#{dir}' does not exist!") unless dir_ok
277
+ dir_ok
278
+ end
279
+
280
+ def check_file(job_id)
281
+ file = @opts['file']
282
+ file_ok = !file || file.start_with?('base64:', 'raw:')
283
+ Chef::Log.error("[#{node_name}] Received commit #{job_id}, but file '#{file}' is a bad format!") unless file_ok
284
+ file_ok
285
+ end
286
+
287
+ def extract_file
288
+ file = @opts['file']
289
+ return nil unless file
290
+ require 'tmpdir'
291
+ dir = client.file_dir
292
+ Dir.mkdir(dir) unless Dir.exists?(dir)
293
+ path = Dir::Tmpname.create('pushy_file', dir){|p| p}
294
+ File.open(path, 'w') do |f|
295
+ type, filedata = file.split(/:/, 2)
296
+ case type
297
+ when "raw"
298
+ f.write(filedata)
299
+ when "base64"
300
+ f.write(Base64.decode64(filedata))
301
+ else
302
+ Chef::Log.error("[#{node_name}] Received commit #{job_id}, but file starting with '#{file.slice(0,80)}' has a bad format!")
303
+ end
304
+ end
305
+ path
306
+ end
307
+ end
308
+ end
@@ -18,6 +18,6 @@
18
18
  # Note: the version must also be updated in
19
19
  # omnibus/config/projects/push-jobs-client.rb
20
20
  class PushyClient
21
- VERSION = "2.100.0"
21
+ VERSION = "3.0.0.pre"
22
22
  PROTOCOL_VERSION = "2.0"
23
23
  end
@@ -17,8 +17,8 @@ Gem::Specification.new do |gem|
17
17
 
18
18
  gem.required_ruby_version = '>= 2.5'
19
19
 
20
- gem.add_dependency "chef", ">= 15.0", "< 17.0"
21
- gem.add_dependency "ohai", ">= 15.0", "< 17.0"
20
+ gem.add_dependency "chef", ">= 14.0", "< 16.0"
21
+ gem.add_dependency "ohai", ">= 14.0", "< 16.0"
22
22
  gem.add_dependency "ffi-rzmq"
23
23
  gem.add_dependency "uuidtools"
24
24
 
@@ -18,6 +18,7 @@ describe PushyClient::ProtocolHandler do
18
18
  allow(client).to receive(:hostname)
19
19
  allow(client).to receive(:org_name)
20
20
  allow(client).to receive(:incarnation_id)
21
+ allow(client).to receive(:max_body_size).and_return(63000)
21
22
  allow_any_instance_of(described_class).to receive(:send_signed_json_command)
22
23
  allow(Chef::Log).to receive(:warn)
23
24
  end
@@ -32,7 +33,7 @@ describe PushyClient::ProtocolHandler do
32
33
 
33
34
  it "logs a warning" do
34
35
  expect(Chef::Log).to receive(:warn).with(
35
- "Command output too long. Will not be sent to server."
36
+ "Command output #{params[:stdout].to_s.bytesize + params[:stderr].to_s.bytesize} is larger than the Client MAX_BODY_SIZE of #{client.max_body_size.to_i}"
36
37
  )
37
38
  send_command
38
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opscode-pushy-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.100.0
4
+ version: 3.0.0.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Anderson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-09 00:00:00.000000000 Z
11
+ date: 2019-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef
@@ -16,40 +16,40 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '15.0'
19
+ version: '14.0'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '17.0'
22
+ version: '16.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '15.0'
29
+ version: '14.0'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '17.0'
32
+ version: '16.0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: ohai
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: '15.0'
39
+ version: '14.0'
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: '17.0'
42
+ version: '16.0'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: '15.0'
49
+ version: '14.0'
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: '17.0'
52
+ version: '16.0'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: ffi-rzmq
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -124,10 +124,11 @@ description: Client for Chef push jobs server
124
124
  email:
125
125
  - mark@chef.io
126
126
  executables:
127
- - pushy-service-manager
127
+ - pushy-client
128
128
  - print_execution_environment
129
129
  - push-apply
130
- - pushy-client
130
+ - pushy-service-manager
131
+ - print_execution_environment~
131
132
  extensions: []
132
133
  extra_rdoc_files: []
133
134
  files:
@@ -135,6 +136,7 @@ files:
135
136
  - LICENSE
136
137
  - Rakefile
137
138
  - bin/print_execution_environment
139
+ - bin/print_execution_environment~
138
140
  - bin/push-apply
139
141
  - bin/pushy-client
140
142
  - bin/pushy-service-manager
@@ -144,6 +146,7 @@ files:
144
146
  - lib/pushy_client/job_runner.rb
145
147
  - lib/pushy_client/periodic_reconfigurer.rb
146
148
  - lib/pushy_client/protocol_handler.rb
149
+ - lib/pushy_client/status_monitor.rb~
147
150
  - lib/pushy_client/version.rb
148
151
  - lib/pushy_client/whitelist.rb
149
152
  - lib/pushy_client/win32.rb
@@ -167,16 +170,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
167
170
  version: '2.5'
168
171
  required_rubygems_version: !ruby/object:Gem::Requirement
169
172
  requirements:
170
- - - ">="
173
+ - - ">"
171
174
  - !ruby/object:Gem::Version
172
- version: '0'
175
+ version: 1.3.1
173
176
  requirements: []
174
- rubygems_version: 3.0.8
177
+ rubyforge_project:
178
+ rubygems_version: 2.7.8
175
179
  signing_key:
176
180
  specification_version: 4
177
181
  summary: Client for Chef push jobs server
178
182
  test_files:
179
- - spec/pushy_client/job_runner_spec.rb
183
+ - spec/spec_helper.rb
180
184
  - spec/pushy_client/whitelist_spec.rb
181
185
  - spec/pushy_client/protocol_handler_spec.rb
182
- - spec/spec_helper.rb
186
+ - spec/pushy_client/job_runner_spec.rb