morpheus-cli 4.2.16 → 4.2.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) 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 +43 -54
  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 +3 -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/boot_scripts_command.rb +1 -1
  22. data/lib/morpheus/cli/cli_command.rb +92 -41
  23. data/lib/morpheus/cli/clusters.rb +0 -18
  24. data/lib/morpheus/cli/commands/standard/benchmark_command.rb +7 -7
  25. data/lib/morpheus/cli/commands/standard/man_command.rb +1 -1
  26. data/lib/morpheus/cli/credentials.rb +13 -9
  27. data/lib/morpheus/cli/deploy.rb +374 -0
  28. data/lib/morpheus/cli/deployments.rb +521 -197
  29. data/lib/morpheus/cli/deploys.rb +271 -126
  30. data/lib/morpheus/cli/doc.rb +182 -0
  31. data/lib/morpheus/cli/error_handler.rb +23 -8
  32. data/lib/morpheus/cli/errors.rb +3 -2
  33. data/lib/morpheus/cli/image_builder_command.rb +2 -2
  34. data/lib/morpheus/cli/instances.rb +136 -17
  35. data/lib/morpheus/cli/invoices_command.rb +51 -38
  36. data/lib/morpheus/cli/library_layouts_command.rb +1 -1
  37. data/lib/morpheus/cli/login.rb +9 -3
  38. data/lib/morpheus/cli/mixins/accounts_helper.rb +158 -100
  39. data/lib/morpheus/cli/mixins/backups_helper.rb +115 -0
  40. data/lib/morpheus/cli/mixins/deployments_helper.rb +135 -0
  41. data/lib/morpheus/cli/mixins/option_source_helper.rb +1 -1
  42. data/lib/morpheus/cli/mixins/print_helper.rb +110 -74
  43. data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -2
  44. data/lib/morpheus/cli/mixins/whoami_helper.rb +19 -6
  45. data/lib/morpheus/cli/network_routers_command.rb +1 -1
  46. data/lib/morpheus/cli/option_parser.rb +48 -5
  47. data/lib/morpheus/cli/option_types.rb +1 -1
  48. data/lib/morpheus/cli/remote.rb +3 -2
  49. data/lib/morpheus/cli/roles.rb +49 -92
  50. data/lib/morpheus/cli/security_groups.rb +7 -1
  51. data/lib/morpheus/cli/service_plans_command.rb +10 -10
  52. data/lib/morpheus/cli/setup.rb +1 -1
  53. data/lib/morpheus/cli/shell.rb +7 -6
  54. data/lib/morpheus/cli/subnets_command.rb +1 -1
  55. data/lib/morpheus/cli/tenants_command.rb +133 -163
  56. data/lib/morpheus/cli/user_groups_command.rb +20 -65
  57. data/lib/morpheus/cli/user_settings_command.rb +115 -13
  58. data/lib/morpheus/cli/user_sources_command.rb +57 -24
  59. data/lib/morpheus/cli/users.rb +210 -186
  60. data/lib/morpheus/cli/version.rb +1 -1
  61. data/lib/morpheus/cli/whitelabel_settings_command.rb +29 -5
  62. data/lib/morpheus/cli/whoami.rb +113 -6
  63. data/lib/morpheus/cli/workflows.rb +1 -1
  64. data/lib/morpheus/ext/hash.rb +21 -0
  65. data/lib/morpheus/terminal.rb +1 -0
  66. metadata +12 -3
  67. data/lib/morpheus/cli/auth_command.rb +0 -105
@@ -609,15 +609,6 @@ class Morpheus::Cli::Clusters
609
609
  option_type_list = ((controller_type['optionTypes'].reject { |type| !type['enabled'] || type['fieldComponent'] } rescue []) + layout['optionTypes'] +
610
610
  (cluster_type['optionTypes'].reject { |type| !type['enabled'] || !type['creatable'] || type['fieldComponent'] } rescue [])).sort { |type| type['displayOrder'] }
611
611
 
612
- # remove volume options if volumes were configured
613
- if !server_payload['volumes'].empty?
614
- option_type_list = reject_volume_option_types(option_type_list)
615
- end
616
- # remove networkId option if networks were configured above
617
- if !server_payload['networkInterfaces'].empty?
618
- option_type_list = reject_networking_option_types(option_type_list)
619
- end
620
-
621
612
  server_payload.deep_merge!(Morpheus::Cli::OptionTypes.prompt(option_type_list, options[:options], @api_client, {zoneId: cloud['id'], siteId: group['id'], layoutId: layout['id']}))
622
613
 
623
614
  # Worker count
@@ -1197,15 +1188,6 @@ class Morpheus::Cli::Clusters
1197
1188
  (type['fieldContext'] == 'instance.networkDomain' && type['fieldName'] == 'id')
1198
1189
  } rescue [])
1199
1190
 
1200
- # remove volume options if volumes were configured
1201
- if !server_payload['volumes'].empty?
1202
- option_type_list = reject_volume_option_types(option_type_list)
1203
- end
1204
- # remove networkId option if networks were configured above
1205
- if !server_payload['networkInterfaces'].empty?
1206
- option_type_list = reject_networking_option_types(option_type_list)
1207
- end
1208
-
1209
1191
  server_payload.deep_merge!(Morpheus::Cli::OptionTypes.prompt(option_type_list, options[:options], @api_client, {zoneId: cloud['id'], siteId: group['id'], layoutId: layout['id']}))
1210
1192
 
1211
1193
  # Create User
@@ -363,7 +363,7 @@ EOT
363
363
  else
364
364
  out << "\texit: 0 "
365
365
  end
366
- 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"
367
367
  out << "\t #{total_time_str.ljust(9, ' ')}"
368
368
  else
369
369
  benchmark_records = []
@@ -384,8 +384,8 @@ EOT
384
384
  # all_durations = benchmark_records.collect {|benchmark_record| benchmark_record.duration }
385
385
  # total_duration = all_durations.inject(0.0) {|acc, i| acc + i }
386
386
  # avg_duration = total_duration / all_durations.size
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"
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"
389
389
 
390
390
  all_durations = []
391
391
  stats = {total: 0, avg: nil, min: nil, max: nil}
@@ -406,10 +406,10 @@ EOT
406
406
  stats[:avg] = stats[:total].to_f / all_durations.size
407
407
  end
408
408
 
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" : ""
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" : ""
413
413
 
414
414
  out = ""
415
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