bosh_cli 1.3215.4.0 → 1.3232.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/cli.rb +1 -0
  3. data/lib/cli/base_command.rb +1 -1
  4. data/lib/cli/basic_login_strategy.rb +3 -3
  5. data/lib/cli/blob_manager.rb +14 -14
  6. data/lib/cli/client/director.rb +16 -5
  7. data/lib/cli/commands/backup.rb +2 -2
  8. data/lib/cli/commands/cloudcheck.rb +2 -2
  9. data/lib/cli/commands/deployment.rb +22 -22
  10. data/lib/cli/commands/deployment_diff.rb +7 -3
  11. data/lib/cli/commands/disks.rb +1 -1
  12. data/lib/cli/commands/errand.rb +2 -2
  13. data/lib/cli/commands/events.rb +55 -0
  14. data/lib/cli/commands/help.rb +1 -1
  15. data/lib/cli/commands/job.rb +3 -3
  16. data/lib/cli/commands/job_management.rb +1 -1
  17. data/lib/cli/commands/locks.rb +6 -4
  18. data/lib/cli/commands/login.rb +1 -1
  19. data/lib/cli/commands/misc.rb +4 -4
  20. data/lib/cli/commands/package.rb +3 -3
  21. data/lib/cli/commands/property_management.rb +8 -8
  22. data/lib/cli/commands/release/delete_release.rb +3 -3
  23. data/lib/cli/commands/release/export_release.rb +2 -2
  24. data/lib/cli/commands/release/finalize_release.rb +26 -12
  25. data/lib/cli/commands/release/upload_release.rb +2 -2
  26. data/lib/cli/commands/release/verify_release.rb +2 -2
  27. data/lib/cli/commands/restore.rb +3 -3
  28. data/lib/cli/commands/snapshot.rb +5 -5
  29. data/lib/cli/commands/ssh.rb +2 -2
  30. data/lib/cli/commands/stemcell.rb +8 -8
  31. data/lib/cli/commands/task.rb +4 -4
  32. data/lib/cli/commands/user.rb +3 -3
  33. data/lib/cli/commands/vms.rb +1 -1
  34. data/lib/cli/config.rb +1 -1
  35. data/lib/cli/core_ext.rb +4 -4
  36. data/lib/cli/deployment_manifest_compiler.rb +1 -1
  37. data/lib/cli/file_with_progress_bar.rb +2 -0
  38. data/lib/cli/job_property_collection.rb +2 -2
  39. data/lib/cli/job_state.rb +2 -2
  40. data/lib/cli/logs_downloader.rb +1 -1
  41. data/lib/cli/manifest.rb +3 -3
  42. data/lib/cli/public_stemcell_presenter.rb +3 -3
  43. data/lib/cli/release.rb +3 -3
  44. data/lib/cli/release_compiler.rb +2 -2
  45. data/lib/cli/release_tarball.rb +2 -1
  46. data/lib/cli/runner.rb +1 -1
  47. data/lib/cli/sorted_release_archiver.rb +1 -12
  48. data/lib/cli/uaa_login_strategy.rb +1 -1
  49. data/lib/cli/version.rb +1 -1
  50. data/lib/cli/versions/local_artifact_storage.rb +2 -2
  51. data/lib/cli/versions/releases_dir_migrator.rb +3 -3
  52. data/lib/cli/versions/version_file_resolver.rb +1 -1
  53. data/lib/cli/versions/versions_index.rb +10 -11
  54. metadata +9 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b3b79ac5a8377c99cad46de0f2f788e3859310d4
4
- data.tar.gz: b8f9b134f53385092d33cf8c2a5ae09ed786ea78
3
+ metadata.gz: f4e190149bfe9bd224bbe10c2be32ad11eeeef44
4
+ data.tar.gz: a43e138fbea15832c7233b3f41dbf7949478a2e2
5
5
  SHA512:
6
- metadata.gz: 8dcc139f7050d386586bf31b9a768f30339deb6da2fd115b692a0477ef0ce1c9bbe2c7931e8fe8abbea010b46416bf7b7f750d633d71dad2f5fa9bef064bf11e
7
- data.tar.gz: 3322806005d9b5560c50cb3d9fe1c5604016f061b5cbf9a3738e140ed36e44f9e9709374ac9560d8ab8fb87cd5c9b657de628821743da02619f94cc9bc461aa9
6
+ metadata.gz: b48f84f6cb4c4659978f95d039616b4bf21f8e04288e17fc9da4b4de4cdb6c506b0af956254698ee9e142c49bd6ec529c4fcc8983a6ed1447b86e18ce068ce4a
7
+ data.tar.gz: 7d53af233d4ecf0320865ca91b6217ebfb51d42e45c5870cfded371d81d5122aaf3833a3e3be6ae2ef28b9aea720f62e5103c321b231e9faec9e5616b1cd7c62
data/lib/cli.rb CHANGED
@@ -43,6 +43,7 @@ end
43
43
 
44
44
  require 'common/common'
45
45
  require 'common/exec'
46
+ require 'common/release/release_directory'
46
47
  require 'common/version/release_version'
47
48
  require 'common/version/release_version_list'
48
49
  require 'common/version/bosh_version'
@@ -208,7 +208,7 @@ module Bosh::Cli
208
208
 
209
209
  def no_track_unsupported
210
210
  if @options.delete(:no_track)
211
- say('Ignoring `' + '--no-track'.make_yellow + "' option")
211
+ say("Ignoring '" + '--no-track'.make_yellow + "' option")
212
212
  end
213
213
  end
214
214
 
@@ -22,7 +22,7 @@ module Bosh
22
22
  end
23
23
 
24
24
  if @director.login(username, password)
25
- @terminal.say_green("Logged in as `#{username}'")
25
+ @terminal.say_green("Logged in as '#{username}'")
26
26
  @config.set_credentials(target, {
27
27
  "username" => username,
28
28
  "password" => password
@@ -30,10 +30,10 @@ module Bosh
30
30
  @config.save
31
31
  else
32
32
  if @interactive
33
- @terminal.say_red("Cannot log in as `#{username}', please try again")
33
+ @terminal.say_red("Cannot log in as '#{username}', please try again")
34
34
  login(target, username, '')
35
35
  else
36
- err("Cannot log in as `#{username}'")
36
+ err("Cannot log in as '#{username}'")
37
37
  end
38
38
  end
39
39
  end
@@ -36,7 +36,7 @@ module Bosh::Cli
36
36
 
37
37
  @src_dir = File.join(@release.dir, "src")
38
38
  unless File.directory?(@src_dir)
39
- err("`src' directory is missing")
39
+ err("'src' directory is missing")
40
40
  end
41
41
 
42
42
  @storage_dir = File.join(@release.dir, ".blobs")
@@ -96,7 +96,7 @@ module Bosh::Cli
96
96
  end
97
97
 
98
98
  nl
99
- say("When ready please run `#{"bosh upload blobs".make_green}'")
99
+ say("When ready please run '#{"bosh upload blobs".make_green}'")
100
100
  end
101
101
 
102
102
  # Registers a file as BOSH blob
@@ -105,11 +105,11 @@ module Bosh::Cli
105
105
  # @return [void]
106
106
  def add_blob(local_path, blob_path)
107
107
  unless File.exists?(local_path)
108
- err("File `#{local_path}' not found")
108
+ err("File '#{local_path}' not found")
109
109
  end
110
110
 
111
111
  if File.directory?(local_path)
112
- err("`#{local_path}' is a directory")
112
+ err("'#{local_path}' is a directory")
113
113
  end
114
114
 
115
115
  if blob_path[0..0] == "/"
@@ -117,19 +117,19 @@ module Bosh::Cli
117
117
  end
118
118
 
119
119
  if blob_path[0..5] == "blobs/"
120
- err("Blob path should not start with `blobs/'")
120
+ err("Blob path should not start with 'blobs/'")
121
121
  end
122
122
 
123
123
  blob_dst = File.join(@blobs_dir, blob_path)
124
124
 
125
125
  if File.directory?(blob_dst)
126
- err("`#{blob_dst}' is a directory, please pick a different path")
126
+ err("'#{blob_dst}' is a directory, please pick a different path")
127
127
  end
128
128
 
129
129
  update = false
130
130
  if File.exists?(blob_dst)
131
131
  if file_checksum(blob_dst) == file_checksum(local_path)
132
- err("Already tracking the same version of `#{blob_path}'")
132
+ err("Already tracking the same version of '#{blob_path}'")
133
133
  end
134
134
  update = true
135
135
  FileUtils.rm(blob_dst)
@@ -145,7 +145,7 @@ module Bosh::Cli
145
145
  end
146
146
 
147
147
  say("When you are done testing the new blob, please run\n" +
148
- "`#{"bosh upload blobs".make_green}' and commit changes.")
148
+ "'#{"bosh upload blobs".make_green}' and commit changes.")
149
149
  end
150
150
 
151
151
  # Synchronizes the contents of blobs directory with blobs index.
@@ -173,7 +173,7 @@ module Bosh::Cli
173
173
  path = strip_blobs_dir(file)
174
174
 
175
175
  if File.exists?(File.join(@src_dir, path))
176
- err("File `#{path}' is in both `blobs' and `src' directory.\n" +
176
+ err("File '#{path}' is in both 'blobs' and 'src' directory.\n" +
177
177
  "Please fix release repo before proceeding")
178
178
  end
179
179
 
@@ -213,7 +213,7 @@ module Bosh::Cli
213
213
  missing_blobs = []
214
214
  @index.each_pair do |path, entry|
215
215
  if File.exists?(File.join(@src_dir, path))
216
- err("File `#{path}' is in both blob index and src directory.\n" +
216
+ err("File '#{path}' is in both blob index and src directory.\n" +
217
217
  "Please fix release repo before proceeding")
218
218
  end
219
219
 
@@ -256,11 +256,11 @@ module Bosh::Cli
256
256
  blob_path = File.join(@blobs_dir, path)
257
257
 
258
258
  unless File.exists?(blob_path)
259
- err("Cannot upload blob, local file `#{blob_path}' doesn't exist")
259
+ err("Cannot upload blob, local file '#{blob_path}' doesn't exist")
260
260
  end
261
261
 
262
262
  if File.symlink?(blob_path)
263
- err("`#{blob_path}' is a symlink")
263
+ err("'#{blob_path}' is a symlink")
264
264
  end
265
265
 
266
266
  checksum = file_checksum(blob_path)
@@ -288,7 +288,7 @@ module Bosh::Cli
288
288
  end
289
289
 
290
290
  unless @index.has_key?(path)
291
- err("Unknown blob path `#{path}'")
291
+ err("Unknown blob path '#{path}'")
292
292
  end
293
293
 
294
294
  blob = @index[path]
@@ -355,7 +355,7 @@ module Bosh::Cli
355
355
  if blob_path[0..blobs_dir.size] == blobs_dir + "/"
356
356
  blob_path[blobs_dir.size+1..-1]
357
357
  else
358
- err("File `#{blob_path}' is not under `blobs' directory")
358
+ err("File '#{blob_path}' is not under 'blobs' directory")
359
359
  end
360
360
  end
361
361
 
@@ -124,6 +124,18 @@ module Bosh
124
124
  get_json('/deployments')
125
125
  end
126
126
 
127
+ def list_events(options={})
128
+ query_string = "/events"
129
+ delimeter = "?"
130
+ [:before_id, :deployment, :instance, :task].each do |param|
131
+ if options[param]
132
+ query_string += "#{delimeter}#{ param.to_s}=#{options[param]}"
133
+ delimeter = "&"
134
+ end
135
+ end
136
+ get_json(query_string)
137
+ end
138
+
127
139
  def list_errands(deployment_name)
128
140
  get_json("/deployments/#{deployment_name}/errands")
129
141
  end
@@ -275,15 +287,14 @@ module Bosh
275
287
  request_and_track(:post, add_query_string(url, extras), options)
276
288
  end
277
289
 
278
- def diff_deployment(name, manifest_yaml, no_redact = false)
279
- redact_param = no_redact ? '?redact=false' : ''
290
+ def diff_deployment(name, manifest_yaml, redact_diff = true)
291
+ redact_param = redact_diff ? '' : '?redact=false'
280
292
  uri = "/deployments/#{name}/diff#{redact_param}"
281
293
  status, body = post(uri, 'text/yaml', manifest_yaml)
282
-
283
294
  if status == 200
284
295
  JSON.parse(body)
285
296
  else
286
- err(parse_error_message(status, body, uri))
297
+ err(parse_error_message(status, body))
287
298
  end
288
299
  end
289
300
 
@@ -411,7 +422,7 @@ module Bosh
411
422
  tmp_file
412
423
  else
413
424
  raise DirectorError,
414
- "Cannot download resource `#{id}': HTTP status #{status}"
425
+ "Cannot download resource '#{id}': HTTP status #{status}"
415
426
  end
416
427
  end
417
428
 
@@ -15,7 +15,7 @@ module Bosh::Cli::Command
15
15
  if status == :done
16
16
  tmp_path = director.fetch_backup
17
17
  FileUtils.mv(tmp_path, path)
18
- say("Backup of BOSH director was put in `#{path.make_green}'.")
18
+ say("Backup of BOSH director was put in '#{path.make_green}'.")
19
19
  else
20
20
  [status, task_id]
21
21
  end
@@ -31,7 +31,7 @@ module Bosh::Cli::Command
31
31
  path = Bosh::Cli::BackupDestinationPath.new(director).create_from_path(dest_path)
32
32
 
33
33
  if File.exists?(path) && !force?
34
- err("There is already an existing file at `#{path}'. " +
34
+ err("There is already an existing file at '#{path}'. " +
35
35
  'To overwrite it use the --force option.')
36
36
  end
37
37
 
@@ -21,8 +21,8 @@ module Bosh::Cli::Command
21
21
 
22
22
  if non_interactive? && !(@report_mode || @auto_mode)
23
23
  err ("Cloudcheck cannot be run in non-interactive mode\n" +
24
- "Please use `--auto' flag if you want automated resolutions " +
25
- "or `--report' if you just want a report of the errors")
24
+ "Please use '--auto' flag if you want automated resolutions " +
25
+ "or '--report' if you just want a report of the errors")
26
26
  end
27
27
 
28
28
  if @auto_mode && @report_mode
@@ -13,7 +13,7 @@ module Bosh::Cli::Command
13
13
  manifest_filename = find_deployment(filename)
14
14
 
15
15
  unless File.exists?(manifest_filename)
16
- err("Missing manifest for `#{filename}'")
16
+ err("Missing manifest for '#{filename}'")
17
17
  end
18
18
 
19
19
  manifest = load_yaml_file(manifest_filename)
@@ -60,10 +60,10 @@ module Bosh::Cli::Command
60
60
  config.target_version = status['version']
61
61
  config.target_uuid = status['uuid']
62
62
  say("#{'WARNING!'.make_red} Your target has been " +
63
- "changed to `#{target.make_red}'!")
63
+ "changed to '#{target.make_red}'!")
64
64
  end
65
65
 
66
- say("Deployment set to `#{manifest_filename.make_green}'")
66
+ say("Deployment set to '#{manifest_filename.make_green}'")
67
67
  config.set_deployment(manifest_filename)
68
68
  config.save
69
69
  end
@@ -81,21 +81,21 @@ module Bosh::Cli::Command
81
81
  usage 'deploy'
82
82
  desc 'Deploy according to the currently selected deployment manifest'
83
83
  option '--recreate', 'Recreate all VMs in deployment'
84
- option '--redact-diff', 'Redact manifest value changes in deployment'
85
- option '--no-redact', 'do not redact'
84
+ option '--no-redact', 'Redact manifest value changes in deployment'
86
85
  option '--skip-drain [job1,job2]', String, 'Skip drain script for either specific or all jobs'
87
86
  def perform
88
87
  auth_required
88
+
89
89
  recreate = !!options[:recreate]
90
- redact_diff = !!options[:redact_diff]
91
- no_redact = !options[:no_redact].nil?
90
+ redact_diff = !!options[:no_redact].nil?
91
+
92
92
  manifest = build_manifest
93
93
 
94
94
  if manifest.hash['releases']
95
95
  manifest.hash['releases'].each do |release|
96
96
  if release['url'].blank?
97
97
  if release['version'] == 'create'
98
- err("Expected URL when specifying release version `create'")
98
+ err("Expected URL when specifying release version 'create'")
99
99
  end
100
100
  else
101
101
  parsed_uri = URI.parse(release['url'])
@@ -110,11 +110,11 @@ module Bosh::Cli::Command
110
110
  run_nested_command "upload", "release", parsed_uri.path, '--name', release['name'], '--version', release['version'].to_s
111
111
  end
112
112
  when 'http', 'https'
113
- err('Path must be a local release directory when version is `create\'') if release['version'] == 'create'
114
- err("Expected SHA1 when specifying remote URL for release `#{release["name"]}'") if release['sha1'].blank?
113
+ err("Path must be a local release directory when version is 'create'") if release['version'] == 'create'
114
+ err("Expected SHA1 when specifying remote URL for release '#{release["name"]}'") if release['sha1'].blank?
115
115
  run_nested_command "upload", "release", release['url'], "--sha1", release['sha1'], "--name", release['name'], "--version", release['version'].to_s
116
116
  else
117
- err("Invalid URL format for release `#{release['name']}' with URL `#{release['url']}'. Supported schemes: file, http, https.")
117
+ err("Invalid URL format for release '#{release['name']}' with URL '#{release['url']}'. Supported schemes: file, http, https.")
118
118
  end
119
119
  end
120
120
  end
@@ -128,11 +128,11 @@ module Bosh::Cli::Command
128
128
  when 'file'
129
129
  run_nested_command "upload", "stemcell", parsed_uri.path, "--name", resource_pool['stemcell']['name'], "--version", resource_pool['stemcell']['version'].to_s, "--skip-if-exists"
130
130
  when 'http', 'https'
131
- err("Expected SHA1 when specifying remote URL for stemcell `#{resource_pool['stemcell']['name']}'") if resource_pool['stemcell']['sha1'].blank?
131
+ err("Expected SHA1 when specifying remote URL for stemcell '#{resource_pool['stemcell']['name']}'") if resource_pool['stemcell']['sha1'].blank?
132
132
  run_nested_command "upload", "stemcell", resource_pool['stemcell']['url'], "--sha1", resource_pool['stemcell']['sha1'],
133
133
  "--name", resource_pool['stemcell']['name'], "--version", resource_pool['stemcell']['version'].to_s, "--skip-if-exists"
134
134
  else
135
- err("Invalid URL format for stemcell `#{resource_pool['stemcell']['name']}' with URL `#{resource_pool['stemcell']['url']}'. Supported schemes: file, http, https.")
135
+ err("Invalid URL format for stemcell '#{resource_pool['stemcell']['name']}' with URL '#{resource_pool['stemcell']['url']}'. Supported schemes: file, http, https.")
136
136
  end
137
137
  end
138
138
  end
@@ -140,7 +140,7 @@ module Bosh::Cli::Command
140
140
 
141
141
  manifest = prepare_deployment_manifest(resolve_properties: true, show_state: true)
142
142
 
143
- context = DeploymentDiff.new(director, manifest).print({redact_diff: redact_diff, no_redact: no_redact})
143
+ context = DeploymentDiff.new(director, manifest).print({redact_diff: redact_diff})
144
144
  say('Please review all changes carefully'.make_yellow) if interactive?
145
145
 
146
146
  header('Deploying')
@@ -160,7 +160,7 @@ module Bosh::Cli::Command
160
160
 
161
161
  status, task_id = director.deploy(manifest.yaml, deploy_options)
162
162
 
163
- task_report(status, task_id, "Deployed `#{manifest.name.make_green}' to `#{target_name.make_green}'")
163
+ task_report(status, task_id, "Deployed '#{manifest.name.make_green}' to '#{target_name.make_green}'")
164
164
  end
165
165
 
166
166
  # bosh delete deployment
@@ -173,7 +173,7 @@ module Bosh::Cli::Command
173
173
 
174
174
  force = !!options[:force]
175
175
 
176
- say("\nYou are going to delete deployment `#{deployment_name}'.".make_red)
176
+ say("\nYou are going to delete deployment '#{deployment_name}'.".make_red)
177
177
  nl
178
178
  say("THIS IS A VERY DESTRUCTIVE OPERATION AND IT CANNOT BE UNDONE!\n".make_red)
179
179
 
@@ -184,9 +184,9 @@ module Bosh::Cli::Command
184
184
 
185
185
  begin
186
186
  status, result = director.delete_deployment(deployment_name, :force => force)
187
- task_report(status, result, "Deleted deployment `#{deployment_name}'")
187
+ task_report(status, result, "Deleted deployment '#{deployment_name}'")
188
188
  rescue Bosh::Cli::ResourceNotFound
189
- task_report(:done, nil, "Skipped delete of missing deployment `#{deployment_name}'")
189
+ task_report(:done, nil, "Skipped delete of missing deployment '#{deployment_name}'")
190
190
  end
191
191
  end
192
192
 
@@ -223,7 +223,7 @@ module Bosh::Cli::Command
223
223
  show_current_state(deployment_name)
224
224
 
225
225
  if save_as && File.exists?(save_as) &&
226
- !confirmed?("Overwrite `#{save_as}'?")
226
+ !confirmed?("Overwrite '#{save_as}'?")
227
227
  err('Please choose another file to save the manifest to')
228
228
  end
229
229
 
@@ -233,7 +233,7 @@ module Bosh::Cli::Command
233
233
  File.open(save_as, 'w') do |f|
234
234
  f.write(deployment['manifest'])
235
235
  end
236
- say("Deployment manifest saved to `#{save_as}'".make_green)
236
+ say("Deployment manifest saved to '#{save_as}'".make_green)
237
237
  else
238
238
  say(deployment['manifest'])
239
239
  end
@@ -244,7 +244,7 @@ module Bosh::Cli::Command
244
244
  config.target = target
245
245
  if config.deployment
246
246
  if interactive?
247
- say("Current deployment is `#{config.deployment.make_green}'")
247
+ say("Current deployment is '#{config.deployment.make_green}'")
248
248
  else
249
249
  say(config.deployment)
250
250
  end
@@ -269,7 +269,7 @@ module Bosh::Cli::Command
269
269
  def path_is_reasonable!(path)
270
270
  #path is actually to a directory, not a file
271
271
  unless File.directory?(path)
272
- err "Path must be a release directory when version is `create'"
272
+ err "Path must be a release directory when version is 'create'"
273
273
  end
274
274
  end
275
275
 
@@ -6,10 +6,12 @@ module Bosh::Cli::Command
6
6
  end
7
7
 
8
8
  def print(options)
9
+ redact_diff = options[:redact_diff]
10
+
9
11
  begin
10
- no_redact = options[:no_redact]
11
- changes = @director.diff_deployment(@manifest.name, @manifest.yaml, no_redact)
12
+ changes = @director.diff_deployment(@manifest.name, @manifest.yaml, redact_diff)
12
13
  diff = changes['diff']
14
+ error = changes['error']
13
15
 
14
16
  header('Detecting deployment changes')
15
17
 
@@ -39,11 +41,13 @@ module Bosh::Cli::Command
39
41
  end
40
42
  end
41
43
 
44
+ say(error) if error
45
+
42
46
  changes['context']
43
47
  rescue Bosh::Cli::ResourceNotFound
44
48
  inspect_deployment_changes(
45
49
  @manifest,
46
- redact_diff: options[:redact_diff]
50
+ redact_diff: redact_diff
47
51
  )
48
52
 
49
53
  nil
@@ -6,7 +6,7 @@ module Bosh::Cli::Command
6
6
  def list
7
7
  auth_required
8
8
  unless options[:orphaned]
9
- err('Only `bosh disks --orphaned` is supported')
9
+ err("Only 'bosh disks --orphaned' is supported")
10
10
  end
11
11
 
12
12
  disks = sort(director.list_orphan_disks)
@@ -44,7 +44,7 @@ module Bosh::Cli::Command
44
44
  status, task_id, errand_result = errands_client.run_errand(deployment_name, errand_name, options[:keep_alive] || FALSE)
45
45
 
46
46
  unless errand_result
47
- task_report(status, task_id, nil, "Errand `#{errand_name}' did not complete")
47
+ task_report(status, task_id, nil, "Errand '#{errand_name}' did not complete")
48
48
  return
49
49
  end
50
50
 
@@ -69,7 +69,7 @@ module Bosh::Cli::Command
69
69
  end
70
70
  end
71
71
 
72
- title_prefix = "Errand `#{errand_name}'"
72
+ title_prefix = "Errand '#{errand_name}'"
73
73
  exit_code_suffix = "(exit code #{errand_result.exit_code})"
74
74
 
75
75
  if errand_result.exit_code == 0