morpheus-cli 4.2.14 → 4.2.19

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/README.md +8 -6
  4. data/lib/morpheus/api/api_client.rb +32 -14
  5. data/lib/morpheus/api/auth_interface.rb +4 -2
  6. data/lib/morpheus/api/backup_jobs_interface.rb +9 -0
  7. data/lib/morpheus/api/backups_interface.rb +16 -0
  8. data/lib/morpheus/api/deploy_interface.rb +25 -56
  9. data/lib/morpheus/api/deployments_interface.rb +44 -55
  10. data/lib/morpheus/api/doc_interface.rb +57 -0
  11. data/lib/morpheus/api/instances_interface.rb +5 -0
  12. data/lib/morpheus/api/rest_interface.rb +40 -0
  13. data/lib/morpheus/api/user_sources_interface.rb +0 -15
  14. data/lib/morpheus/api/users_interface.rb +2 -3
  15. data/lib/morpheus/benchmarking.rb +2 -2
  16. data/lib/morpheus/cli.rb +4 -1
  17. data/lib/morpheus/cli/access_token_command.rb +27 -10
  18. data/lib/morpheus/cli/apps.rb +21 -15
  19. data/lib/morpheus/cli/backup_jobs_command.rb +276 -0
  20. data/lib/morpheus/cli/backups_command.rb +271 -0
  21. data/lib/morpheus/cli/blueprints_command.rb +27 -61
  22. data/lib/morpheus/cli/boot_scripts_command.rb +1 -1
  23. data/lib/morpheus/cli/cli_command.rb +183 -45
  24. data/lib/morpheus/cli/cli_registry.rb +3 -0
  25. data/lib/morpheus/cli/clouds.rb +7 -10
  26. data/lib/morpheus/cli/clusters.rb +0 -18
  27. data/lib/morpheus/cli/commands/standard/benchmark_command.rb +23 -20
  28. data/lib/morpheus/cli/commands/standard/man_command.rb +1 -1
  29. data/lib/morpheus/cli/credentials.rb +13 -9
  30. data/lib/morpheus/cli/deploy.rb +374 -0
  31. data/lib/morpheus/cli/deployments.rb +521 -197
  32. data/lib/morpheus/cli/deploys.rb +271 -126
  33. data/lib/morpheus/cli/doc.rb +182 -0
  34. data/lib/morpheus/cli/error_handler.rb +23 -8
  35. data/lib/morpheus/cli/errors.rb +3 -2
  36. data/lib/morpheus/cli/image_builder_command.rb +2 -2
  37. data/lib/morpheus/cli/instances.rb +136 -17
  38. data/lib/morpheus/cli/invoices_command.rb +339 -225
  39. data/lib/morpheus/cli/jobs_command.rb +2 -2
  40. data/lib/morpheus/cli/library_layouts_command.rb +1 -1
  41. data/lib/morpheus/cli/library_option_lists_command.rb +61 -125
  42. data/lib/morpheus/cli/library_option_types_command.rb +32 -37
  43. data/lib/morpheus/cli/login.rb +9 -3
  44. data/lib/morpheus/cli/mixins/accounts_helper.rb +158 -100
  45. data/lib/morpheus/cli/mixins/backups_helper.rb +115 -0
  46. data/lib/morpheus/cli/mixins/deployments_helper.rb +135 -0
  47. data/lib/morpheus/cli/mixins/library_helper.rb +32 -0
  48. data/lib/morpheus/cli/mixins/option_source_helper.rb +1 -1
  49. data/lib/morpheus/cli/mixins/print_helper.rb +149 -84
  50. data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -2
  51. data/lib/morpheus/cli/mixins/whoami_helper.rb +19 -6
  52. data/lib/morpheus/cli/network_routers_command.rb +1 -1
  53. data/lib/morpheus/cli/option_parser.rb +48 -5
  54. data/lib/morpheus/cli/option_types.rb +46 -10
  55. data/lib/morpheus/cli/price_sets_command.rb +1 -1
  56. data/lib/morpheus/cli/projects_command.rb +7 -7
  57. data/lib/morpheus/cli/remote.rb +3 -2
  58. data/lib/morpheus/cli/roles.rb +49 -92
  59. data/lib/morpheus/cli/security_groups.rb +7 -1
  60. data/lib/morpheus/cli/service_plans_command.rb +10 -10
  61. data/lib/morpheus/cli/setup.rb +1 -1
  62. data/lib/morpheus/cli/shell.rb +7 -6
  63. data/lib/morpheus/cli/subnets_command.rb +1 -1
  64. data/lib/morpheus/cli/tasks.rb +24 -10
  65. data/lib/morpheus/cli/tenants_command.rb +133 -163
  66. data/lib/morpheus/cli/user_groups_command.rb +20 -65
  67. data/lib/morpheus/cli/user_settings_command.rb +115 -13
  68. data/lib/morpheus/cli/user_sources_command.rb +57 -24
  69. data/lib/morpheus/cli/users.rb +210 -186
  70. data/lib/morpheus/cli/version.rb +1 -1
  71. data/lib/morpheus/cli/whitelabel_settings_command.rb +29 -5
  72. data/lib/morpheus/cli/whoami.rb +113 -6
  73. data/lib/morpheus/cli/workflows.rb +11 -8
  74. data/lib/morpheus/ext/hash.rb +21 -0
  75. data/lib/morpheus/formatters.rb +7 -19
  76. data/lib/morpheus/terminal.rb +1 -0
  77. metadata +12 -3
  78. data/lib/morpheus/cli/auth_command.rb +0 -105
@@ -7,11 +7,14 @@ class Morpheus::Cli::BenchmarkCommand
7
7
  include Morpheus::Cli::CliCommand
8
8
  set_command_name :'benchmark'
9
9
 
10
- # control global benchmark toggle
11
- register_subcommands :on, :off, :on?, :off?
12
- # record your own benchmarks
13
- register_subcommands :start, :stop, :status, :exec => :execute
14
-
10
+ register_subcommand :on, "Enable global benchmarking."
11
+ register_subcommand :off, "Disable global benchmarking."
12
+ register_subcommand :on?, "Print the value of the global benchmark setting. Exit 0 if on."
13
+ register_subcommand :off?, "Print the value of the global benchmark setting. Exit 0 if off."
14
+ register_subcommand :start, "Start recording a benchmark."
15
+ register_subcommand :stop, "Stop recording a benchmark."
16
+ register_subcommand :status, "Print status of benchmark."
17
+ register_subcommand :exec, :execute, "Benchmark a specified command or expression."
15
18
 
16
19
  # this would be cool, we should store all benchmarking results in memory or on disk =o
17
20
  # register_subcommands :list, :get, :put, :remove
@@ -35,7 +38,7 @@ class Morpheus::Cli::BenchmarkCommand
35
38
  opts.banner = subcommand_usage("")
36
39
  build_common_options(opts, options, [:quiet])
37
40
  opts.footer = <<-EOT
38
- Enable global benchmarking.
41
+ #{subcommand_description}
39
42
  This behaves the same as if you were to add the -B switch to every command.
40
43
  EOT
41
44
  end
@@ -65,7 +68,7 @@ EOT
65
68
  opts.banner = subcommand_usage("")
66
69
  build_common_options(opts, options, [:quiet])
67
70
  opts.footer = <<-EOT
68
- Disable global benchmarking.
71
+ #{subcommand_description}
69
72
  The default state for this setting is off.
70
73
  EOT
71
74
  end
@@ -95,7 +98,7 @@ EOT
95
98
  opts.banner = subcommand_usage("")
96
99
  build_common_options(opts, options, [:quiet])
97
100
  opts.footer = <<-EOT
98
- Print the value of the global benchmark setting.
101
+ #{subcommand_description}
99
102
  Exit 0 if on.
100
103
  EOT
101
104
  end
@@ -121,7 +124,7 @@ EOT
121
124
  opts.banner = subcommand_usage("")
122
125
  build_common_options(opts, options, [:quiet])
123
126
  opts.footer = <<-EOT
124
- Print the value of the global benchmark setting.
127
+ #{subcommand_description}
125
128
  Exit 0 if off.
126
129
  EOT
127
130
  end
@@ -149,7 +152,7 @@ EOT
149
152
  opts.banner = subcommand_usage("[name]")
150
153
  build_common_options(opts, options, [:quiet])
151
154
  opts.footer = <<-EOT
152
- Start recording a benchmark.
155
+ #{subcommand_description}
153
156
  [name] is required. This is just a name for the routine.
154
157
  This allows you to record how long it takes to run a series of commands.
155
158
  Just run `benchmark stop` when you are finished.
@@ -194,7 +197,7 @@ EOT
194
197
  end
195
198
  build_common_options(opts, options, [:quiet])
196
199
  opts.footer = <<-EOT
197
- Stop recording a benchmark.
200
+ #{subcommand_description}
198
201
  [name] is optional. This is the name of the benchmark to stop.
199
202
  The last benchmark is used by default.
200
203
  EOT
@@ -254,7 +257,7 @@ EOT
254
257
  opts.banner = subcommand_usage("[name]")
255
258
  build_common_options(opts, options, [:quiet])
256
259
  opts.footer = <<-EOT
257
- Print status of benchmark.
260
+ #{subcommand_description}
258
261
  [name] is optional. This is the name of the benchmark to inspect.
259
262
  The last benchmark is used by default.
260
263
  EOT
@@ -309,7 +312,7 @@ EOT
309
312
  end
310
313
  build_common_options(opts, options, [:quiet])
311
314
  opts.footer = <<-EOT
312
- Benchmark a specified command.
315
+ #{subcommand_description}
313
316
  [command] is required. This is the command to execute
314
317
  EOT
315
318
  end
@@ -360,7 +363,7 @@ EOT
360
363
  else
361
364
  out << "\texit: 0 "
362
365
  end
363
- total_time_str = "#{benchmark_record.duration.round((benchmark_record.duration > 0.002) ? 3 : 6)}s"
366
+ total_time_str = "#{benchmark_record.duration.round((benchmark_record.duration > 0.002) ? 3 : 6)} s"
364
367
  out << "\t #{total_time_str.ljust(9, ' ')}"
365
368
  else
366
369
  benchmark_records = []
@@ -381,8 +384,8 @@ EOT
381
384
  # all_durations = benchmark_records.collect {|benchmark_record| benchmark_record.duration }
382
385
  # total_duration = all_durations.inject(0.0) {|acc, i| acc + i }
383
386
  # avg_duration = total_duration / all_durations.size
384
- # total_time_str = "#{total_duration.round((total_duration > 0.002) ? 3 : 6)}s"
385
- # avg_time_str = "#{avg_duration.round((total_duration > 0.002) ? 3 : 6)}s"
387
+ # total_time_str = "#{total_duration.round((total_duration > 0.002) ? 3 : 6)} s"
388
+ # avg_time_str = "#{avg_duration.round((total_duration > 0.002) ? 3 : 6)} s"
386
389
 
387
390
  all_durations = []
388
391
  stats = {total: 0, avg: nil, min: nil, max: nil}
@@ -403,10 +406,10 @@ EOT
403
406
  stats[:avg] = stats[:total].to_f / all_durations.size
404
407
  end
405
408
 
406
- total_time_str = "#{stats[:total].round((stats[:total] > 0.002) ? 3 : 6)}s"
407
- min_time_str = stats[:min] ? "#{stats[:min].round((stats[:min] > 0.002) ? 3 : 6)}s" : ""
408
- max_time_str = stats[:max] ? "#{stats[:max].round((stats[:max] > 0.002) ? 3 : 6)}s" : ""
409
- avg_time_str = stats[:avg] ? "#{stats[:avg].round((stats[:avg] > 0.002) ? 3 : 6)}s" : ""
409
+ total_time_str = "#{stats[:total].round((stats[:total] > 0.002) ? 3 : 6)} s"
410
+ min_time_str = stats[:min] ? "#{stats[:min].round((stats[:min] > 0.002) ? 3 : 6)} s" : ""
411
+ max_time_str = stats[:max] ? "#{stats[:max].round((stats[:max] > 0.002) ? 3 : 6)} s" : ""
412
+ avg_time_str = stats[:avg] ? "#{stats[:avg].round((stats[:avg] > 0.002) ? 3 : 6)} s" : ""
410
413
 
411
414
  out = ""
412
415
  # <benchmark name or command>
@@ -176,7 +176,7 @@ morpheus v#{Morpheus::Cli::VERSION}
176
176
 
177
177
  To learn more about the Morpheus Appliance, visit https://www.morpheusdata.com/features
178
178
 
179
- To learn more about the Morpheus API, visit http://bertramdev.github.io/morpheus-apidoc/
179
+ To learn more about the Morpheus API, visit https://apidocs.morpheusdata.com
180
180
 
181
181
  ## GLOBAL OPTIONS
182
182
 
@@ -87,7 +87,7 @@ module Morpheus
87
87
  rescue ::RestClient::Exception => e
88
88
  #raise e
89
89
  print_red_alert "Token not valid."
90
- if options[:debug] || options[:debug]
90
+ if options[:debug]
91
91
  print_rest_exception(e, options)
92
92
  end
93
93
  wallet = nil
@@ -255,8 +255,7 @@ module Morpheus
255
255
  true
256
256
  end
257
257
 
258
- def use_refresh_token(options = {})
259
- #puts "use_refresh_token(#{options})"
258
+ def use_refresh_token(refresh_token_value, options = {})
260
259
 
261
260
  wallet = load_saved_credentials
262
261
 
@@ -265,22 +264,25 @@ module Morpheus
265
264
  return nil
266
265
  end
267
266
 
268
- if wallet['refresh_token'].nil?
269
- print_red_alert yellow,"No refresh token found for #{display_appliance(@appliance_name, @appliance_url)}",reset,"\n"
270
- return nil
267
+ if refresh_token_value.nil?
268
+ if wallet['refresh_token']
269
+ refresh_token_value = wallet['refresh_token']
270
+ else
271
+ print_red_alert yellow,"No refresh token found for #{display_appliance(@appliance_name, @appliance_url)}",reset,"\n"
272
+ return nil
273
+ end
271
274
  end
272
275
 
273
-
274
276
  username = wallet['username']
275
277
 
276
278
  begin
277
279
  auth_interface = Morpheus::AuthInterface.new({url:@appliance_url})
278
280
  auth_interface.setopts(options)
279
281
  if options[:dry_run]
280
- print_dry_run auth_interface.dry.use_refresh_token(wallet['refresh_token'])
282
+ print_dry_run auth_interface.dry.use_refresh_token(refresh_token_value)
281
283
  return nil
282
284
  end
283
- json_response = auth_interface.use_refresh_token(wallet['refresh_token'])
285
+ json_response = auth_interface.use_refresh_token(refresh_token_value)
284
286
  #wallet = json_response
285
287
  login_date = Time.now
286
288
  expire_date = nil
@@ -311,6 +313,8 @@ module Morpheus
311
313
  print_rest_exception(e, options)
312
314
  end
313
315
  wallet = nil
316
+ # return now or else it will log them out
317
+ return nil
314
318
  end
315
319
 
316
320
  # save wallet to credentials file
@@ -0,0 +1,374 @@
1
+ require 'morpheus/cli/cli_command'
2
+ require 'yaml'
3
+
4
+ class Morpheus::Cli::Deploy
5
+ include Morpheus::Cli::CliCommand
6
+ include Morpheus::Cli::DeploymentsHelper
7
+
8
+ set_command_name :deploy
9
+
10
+ def connect(opts)
11
+ @api_client = establish_remote_appliance_connection(opts)
12
+ @instances_interface = @api_client.instances
13
+ @deploy_interface = @api_client.deploy
14
+ @deployments_interface = @api_client.deployments
15
+ end
16
+
17
+ def handle(args)
18
+ options={}
19
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
20
+ opts.banner = "Usage: morpheus deploy [environment]"
21
+ build_common_options(opts, options, [:auto_confirm, :remote, :dry_run])
22
+ opts.footer = <<-EOT
23
+ Deploy to an instance using the morpheus.yml file, located in the working directory.
24
+ [environment] is optional. Merge settings under environments.{environment}. Default is no environment.
25
+
26
+ First this parses the morpheus.yml file and merges the specified environment settings.
27
+ The specified instance must exist and the specified version must not exist.
28
+ If the settings are valid, the new deployment version will be created and
29
+ all the specified files are uploaded to the new deployment version.
30
+ Finally, it deploys the new version to the instance.
31
+
32
+ The morpheus.yml should be located in the working directory.
33
+ This file contains the information necessary to perform a deployment via the cli.
34
+
35
+ File Settings
36
+ ==================
37
+
38
+ * name - (required) The instance name we are deploying to and, by default, name of the deployment being created.
39
+ * version - (required) The version identifier of the deployment being created (userVersion)
40
+ * deployment - The name of the deployment being created, name is used by default
41
+ * script - The initial script to run before looking for files to upload.
42
+ * files - List of file patterns to use for uploading files and their target destination.
43
+ Each item should contain path and pattern, path may be relative to the working directory, default pattern is: '**/*'
44
+ * options - Map of deployment options depending on deployment type
45
+ * post_script - A post operation script to be run on the local machine
46
+ * stage_only - If set to true the deploy will only be staged and not actually run
47
+ * environments - Map of objects that contain nested properties for each environment name
48
+
49
+ It is possible to nest these properties in an "environments" map to override based on a passed environment.
50
+
51
+ Example
52
+ ==================
53
+
54
+ name: neatsite
55
+ version: 5.0
56
+ script: "rake build"
57
+ files:
58
+ - path: build
59
+ environments:
60
+ production:
61
+ files:
62
+ - path: production-build
63
+ EOT
64
+ end
65
+ optparse.parse!(args)
66
+ verify_args!(args:args, optparse:optparse, min:0, max:1)
67
+ options[:options]['name'] = args[0] if args[0]
68
+ connect(options)
69
+ payload = {}
70
+
71
+ environment = default_deploy_environment
72
+ if args.count > 0
73
+ environment = args[0]
74
+ end
75
+ if load_deploy_file().nil?
76
+ raise_command_error "Morpheus Deploy File `morpheus.yml` not detected. Please create one and try again."
77
+ end
78
+
79
+ # Parse and validate config, need instance + deployment + version + files
80
+ # name can be specified as a single value for both instance and deployment
81
+
82
+ deploy_args = merged_deploy_args(environment)
83
+
84
+ instance_name = deploy_args['name']
85
+ if deploy_args['instance'].is_a?(String)
86
+ instance_name = deploy_args['instance']
87
+ end
88
+ if instance_name.nil?
89
+ raise_command_error "Instance not specified. Please specify the instance name and try again."
90
+ end
91
+
92
+ deployment_name = deploy_args['name'] || instance_name
93
+ if deploy_args['deployment'].is_a?(String)
94
+ deployment_name = deploy_args['deployment']
95
+ end
96
+
97
+ version_number = deploy_args['version']
98
+ if version_number.nil?
99
+ raise_command_error "Version not specified. Please specify the version and try again."
100
+ end
101
+
102
+ instance_results = @instances_interface.list(name: instance_name)
103
+ if instance_results['instances'].empty?
104
+ raise_command_error "Instance not found by name '#{instance_name}'"
105
+ end
106
+ instance = instance_results['instances'][0]
107
+ instance_id = instance['id']
108
+
109
+ # ok do it
110
+ # fetch/create deployment, create deployment version, upload files, and deploy it to instance.
111
+
112
+ print_h1 "Morpheus Deployment"
113
+
114
+ columns = {
115
+ "Instance" => :name,
116
+ "Deployment" => :deployment,
117
+ "Version" => :version,
118
+ "Script" => :script,
119
+ "Post Script" => :post_script,
120
+ "Files" => :files,
121
+ "Environment" => :environment,
122
+ }
123
+ pretty_file_config = deploy_args['files'].collect {|it|
124
+ [(it['path'] ? "path: #{it['path']}" : nil), (it['pattern'] ? "pattern: #{it['pattern']}" : nil)].compact.join(", ")
125
+ }.join(", ")
126
+ deploy_settings = {
127
+ :name => instance_name,
128
+ :deployment => deployment_name,
129
+ :version => version_number,
130
+ :script => deploy_args['script'],
131
+ :post_script => deploy_args['post_script'],
132
+ :files => pretty_file_config,
133
+ # :files => deploy_args['files'],
134
+ # :files => deploy_files.size,
135
+ # :file_config => (deploy_files.size == 1 ? deploy_files[0][:destination] : deploy_args['files'])
136
+ :environment => environment
137
+ }
138
+ columns.delete("Script") if deploy_settings[:script].nil?
139
+ columns.delete("Post Script") if deploy_settings[:post_script].nil?
140
+ columns.delete("Environment") if deploy_settings[:environment].nil?
141
+ print_description_list(columns, deploy_settings)
142
+ print reset, "\n"
143
+
144
+ if !deploy_args['script'].nil?
145
+ # do this for dry run too since this is usually what creates the files to be uploaded
146
+ print cyan, "Executing Pre Deploy Script...", reset, "\n"
147
+ puts "running command: #{deploy_args['script']}"
148
+ if !system(deploy_args['script'])
149
+ raise_command_error "Error executing pre script..."
150
+ end
151
+ end
152
+
153
+ # Find Files to Upload
154
+ deploy_files = []
155
+ if deploy_args['files'].nil? || deploy_args['files'].empty? || !deploy_args['files'].is_a?(Array)
156
+ raise_command_error "Files not specified. Please specify files array, each item may specify a path or pattern of file(s) to upload"
157
+ else
158
+ #print "\n",cyan, "Finding Files...", reset, "\n"
159
+ current_working_dir = Dir.pwd
160
+ deploy_args['files'].each do |fmap|
161
+ Dir.chdir(fmap['path'] || current_working_dir)
162
+ files = Dir.glob(fmap['pattern'] || '**/*')
163
+ files.each do |file|
164
+ if File.file?(file)
165
+ destination = file.split("/")[0..-2].join("/")
166
+ # deploy_files << {filepath: File.expand_path(file), destination: destination}
167
+ deploy_files << {filepath: File.expand_path(file), destination: file}
168
+ end
169
+ end
170
+ end
171
+ #print cyan, "Found #{deploy_files.size} Files to Upload!", reset, "\n"
172
+ Dir.chdir(current_working_dir)
173
+ end
174
+
175
+ if deploy_files.empty?
176
+ raise_command_error "0 files found for: #{deploy_args['files'].inspect}"
177
+ else
178
+ print cyan, "Found #{deploy_files.size} Files to Upload!", reset, "\n"
179
+ end
180
+
181
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to create deployment version #{version_number} (#{deploy_files.size} #{deploy_files.size == 1 ? 'file' : 'files'}) and deploy it to instance #{instance['name']}?")
182
+ return 9, "aborted command"
183
+ end
184
+
185
+ # Find or Create Deployment
186
+ deployment = nil
187
+ deployments = @deployments_interface.list(name: deployment_name)['deployments']
188
+
189
+ @instances_interface.setopts(options)
190
+ @deploy_interface.setopts(options)
191
+ @deployments_interface.setopts(options)
192
+
193
+ if deployments.size > 1
194
+ raise_command_error "#{deployments.size} deployment versions found by deployment '#{name}'"
195
+ elsif deployments.size == 1
196
+ deployment = deployments[0]
197
+ # should update here, eg description
198
+ else
199
+ # create it
200
+ payload = {
201
+ 'deployment' => {
202
+ 'name' => deployment_name
203
+ }
204
+ }
205
+ payload['deployment']['description'] = deploy_args['description'] if deploy_args['description']
206
+
207
+ if options[:dry_run]
208
+ print_dry_run @deployments_interface.dry.create(payload)
209
+ # return 0, nil
210
+ deployment = {'id' => ':deploymentId', 'name' => deployment_name}
211
+ else
212
+ json_response = @deployments_interface.create(payload)
213
+ deployment = json_response['deployment']
214
+ end
215
+ end
216
+
217
+ # Find or Create Deployment Version
218
+ # Actually, for now this this errors if the version already exists, but it should update it.
219
+
220
+ @deployments_interface = @api_client.deployments
221
+ deployment_version = nil
222
+ if options[:dry_run]
223
+ print_dry_run @deployments_interface.dry.list_versions(deployment['id'], {userVersion: version_number})
224
+ # return 0, nil
225
+ #deployment_versions =[{'id' => ':versionId', 'version' => version_number}]
226
+ deployment_versions = []
227
+ else
228
+ deployment_versions = @deployments_interface.list_versions(deployment['id'], {userVersion: version_number})['versions']
229
+ @deployments_interface.setopts(options)
230
+ end
231
+
232
+
233
+ if deployment_versions.size > 0
234
+ raise_command_error "Deployment '#{deployment['name']}' version '#{version_number}' already exists. Specify a new version or delete the existing version."
235
+ # if deployment_versions.size > 1
236
+ # raise_command_error "#{deployment_versions.size} versions found by version '#{name}'"
237
+ # elsif deployment_versions.size == 1
238
+ # deployment_version = deployment_versions[0]
239
+ # # should update here, eg description
240
+ else
241
+ # create it
242
+ payload = {
243
+ 'version' => {
244
+ 'userVersion' => version_number,
245
+ 'deployType' => (deploy_args['type'] || deploy_args['deployType'] || 'file')
246
+ }
247
+ }
248
+ payload['version']['fetchUrl'] = deploy_args['fetchUrl'] if deploy_args['fetchUrl']
249
+ payload['version']['gitUrl'] = deploy_args['gitUrl'] if deploy_args['gitUrl']
250
+ payload['version']['gitRef'] = deploy_args['gitRef'] if deploy_args['gitRef']
251
+
252
+ if options[:dry_run]
253
+ print_dry_run @deployments_interface.dry.create_version(deployment['id'], payload)
254
+ # return 0, nil
255
+ deployment_version = {'id' => ':versionId', 'version' => version_number}
256
+ else
257
+ json_response = @deployments_interface.create_version(deployment['id'], payload)
258
+ deployment_version = json_response['version']
259
+ end
260
+ end
261
+
262
+
263
+ # Upload Files
264
+ if deploy_files && !deploy_files.empty?
265
+ print "\n",cyan, "Uploading #{deploy_files.size} Files...", reset, "\n"
266
+ current_working_dir = Dir.pwd
267
+ deploy_files.each do |f|
268
+ destination = f[:destination]
269
+ if options[:dry_run]
270
+ print_dry_run @deployments_interface.upload_file(deployment['id'], deployment_version['id'], f[:filepath], f[:destination])
271
+ else
272
+ print cyan," - Uploading #{f[:destination]} ...", reset if !options[:quiet]
273
+ upload_result = @deployments_interface.upload_file(deployment['id'], deployment_version['id'], f[:filepath], f[:destination])
274
+ #print green + "SUCCESS" + reset + "\n" if !options[:quiet]
275
+ print reset, "\n" if !options[:quiet]
276
+ end
277
+ end
278
+ print cyan, "Upload Complete!", reset, "\n"
279
+ Dir.chdir(current_working_dir)
280
+ else
281
+ print "\n",cyan, "0 files to upload", reset, "\n"
282
+ end
283
+
284
+ # TODO: support deploying other deployTypes too, git and fetch
285
+
286
+ if !deploy_args['post_script'].nil?
287
+ print cyan, "Executing Post Script...", reset, "\n"
288
+ puts "running command: #{deploy_args['post_script']}"
289
+ if !system(deploy_args['post_script'])
290
+ raise_command_error "Error executing post script..."
291
+ end
292
+ end
293
+
294
+ # JD: restart for evars eh?
295
+ if deploy_args['env']
296
+ evars = []
297
+ deploy_args['env'].each_pair do |key, value|
298
+ evars << {name: key, value: value, export: false}
299
+ end
300
+ payload = {envs: evars}
301
+ if options[:dry_run]
302
+ print_dry_run @instances_interface.dry.create_env(instance_id, payload)
303
+ print_dry_run @instances_interface.dry.restart(instance_id)
304
+ else
305
+ @instances_interface.create_env(instance_id, payload)
306
+ @instances_interface.restart(instance_id)
307
+ end
308
+ end
309
+ # Create the AppDeploy, this does the deploy async (as of 4.2.2-3)
310
+ payload = {'appDeploy' => {} }
311
+ payload['appDeploy']['versionId'] = deployment_version['id']
312
+ if deploy_args['options']
313
+ payload['appDeploy']['config'] = deploy_args['options']
314
+ end
315
+ # stageOnly means do not actually deploy yet, can invoke @deploy_interface.deploy(deployment['id']) later
316
+ # there is no cli command for that yet though..
317
+ stage_only = deploy_args['stage_deploy'] || deploy_args['stage_only'] || deploy_args['stageOnly']
318
+ if stage_only
319
+ payload['appDeploy']['stageOnly'] = true
320
+ end
321
+ app_deploy_id = nil
322
+ if options[:dry_run]
323
+ print_dry_run @deploy_interface.dry.create(instance_id, payload)
324
+ # return 0, nil
325
+ app_deploy_id = ':appDeployId'
326
+ else
327
+ # Create a new appDeploy record, without stageOnly, this actually does the deployment
328
+ print cyan, "Deploying #{deployment_name} version #{version_number} to instance #{instance_name} ...", reset, "\n"
329
+ deploy_result = @deploy_interface.create(instance_id, payload)
330
+ app_deploy = deploy_result['appDeploy']
331
+ app_deploy_id = app_deploy['id']
332
+ print_green_success "Deploy Successful!"
333
+ end
334
+ return 0, nil
335
+ end
336
+
337
+ protected
338
+
339
+ # Loads a morpheus.yml file from within the current working directory.
340
+ # This file contains information necessary to perform a deployment via the cli.
341
+ #
342
+ # === Example File Attributes
343
+ # * +script+ - The initial script to run before uploading files
344
+ # * +name+ - The instance name we are deploying to (can be overridden in CLI)
345
+ # * +files+ - List of file patterns to use for uploading files and their target destination
346
+ # * +options+ - Map of deployment options depending on deployment type
347
+ # * +post_script+ - A post operation script to be run on the local machine
348
+ # * +stage_deploy+ - If set to true the deploy will only be staged and not actually run
349
+ #
350
+ # +NOTE: + It is also possible to nest these properties in an "environments" map to override based on a passed environment deploy name
351
+ #
352
+ def load_deploy_file
353
+ if !File.exist? "morpheus.yml"
354
+ puts "No morpheus.yml file detected in the current directory. Nothing to do."
355
+ return nil
356
+ end
357
+
358
+ @deploy_file = YAML.load_file("morpheus.yml")
359
+ return @deploy_file
360
+ end
361
+
362
+ def merged_deploy_args(environment)
363
+ deploy_args = @deploy_file.reject { |key,value| key == 'environment'}
364
+ if environment && !@deploy_file['environment'].nil? && !@deploy_file['environment'][environment].nil?
365
+ deploy_args = deploy_args.merge(@deploy_file['environment'][environment])
366
+ end
367
+ return deploy_args
368
+ end
369
+
370
+ def default_deploy_environment
371
+ nil
372
+ end
373
+
374
+ end