vcoworkflows-ruby2 0.2.3

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.
@@ -0,0 +1,44 @@
1
+ require 'vcoworkflows'
2
+
3
+ # rubocop:disable all
4
+ module VcoWorkflows
5
+ # wrapper to assist aruba in single process execution
6
+ class Runner
7
+ # Allow everything fun to be injected from the outside while defaulting to normal implementations.
8
+ def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
9
+ @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
10
+ end
11
+
12
+ # Do the things!
13
+ def execute!
14
+ exit_code = begin
15
+ # Thor accesses these streams directly rather than letting them be
16
+ # injected, so we replace them...
17
+ $stderr = @stderr
18
+ $stdin = @stdin
19
+ $stdout = @stdout
20
+
21
+ VcoWorkflows::CLI.start(@argv)
22
+
23
+ # Thor::Base#start does not have a return value, assume success if no
24
+ # exception is raised.
25
+ 0
26
+ rescue StandardError => e
27
+ # The ruby interpreter would pipe this to STDERR and exit 1 in the
28
+ # case of an unhandled exception
29
+ b = e.backtrace
30
+ b.unshift("#{b.shift}: #{e.message} (#{e.class})")
31
+ @stderr.puts(b.map { |s| "\tfrom #{s}" }.join("\n"))
32
+ 1
33
+ ensure
34
+ # put them back.
35
+ $stderr = STDERR
36
+ $stdin = STDIN
37
+ $stdout = STDOUT
38
+ end
39
+ # Proxy exit code back to the injected kernel.
40
+ @kernel.exit(exit_code)
41
+ end
42
+ end
43
+ end
44
+ # rubocop:enable all
@@ -0,0 +1,71 @@
1
+ require 'vcoworkflows/constants'
2
+ require 'vcoworkflows/config'
3
+ require 'rest_client'
4
+
5
+ # VcoWorkflows
6
+ module VcoWorkflows
7
+ # VcoSession is a simple wrapper for RestClient::Resource, and supports
8
+ # GET and POST operations against the vCO API.
9
+ class VcoSession
10
+ # Accessor for rest-client object, primarily for testing purposes
11
+ attr_reader :rest_resource
12
+
13
+ # rubocop:disable MethodLength
14
+
15
+ # Initialize the session
16
+ #
17
+ # When specifying a config, do not provide other parameters. Likewise,
18
+ # if providing uri, user, and password, a config object is not necessary.
19
+ #
20
+ # @param [VcoWorkflows::Config] config Configuration object for the connection
21
+ # @param [String] uri URI for the vCenter Orchestrator API endpoint
22
+ # @param [String] user User name for vCO
23
+ # @param [String] password Password for vCO
24
+ # @param [Boolean] verify_ssl Whether or not to verify SSL certificates
25
+ def initialize(config: nil, uri: nil, user: nil, password: nil, verify_ssl: true)
26
+ # If a configuration object was provided, use it.
27
+ # If we got a URL and no config, build a new config with the URL and any
28
+ # other options that passed in.
29
+ # Otherwise, load the default config file if possible...
30
+ if config
31
+ config = config
32
+ elsif uri && config.nil?
33
+ config = VcoWorkflows::Config.new(url: uri,
34
+ username: user,
35
+ password: password,
36
+ verify_ssl: verify_ssl)
37
+ elsif uri.nil? && config.nil?
38
+ config = VcoWorkflows::Config.new
39
+ end
40
+
41
+ RestClient.proxy = ENV['http_proxy'] # Set a proxy if present
42
+ @rest_resource = RestClient::Resource.new(config.url,
43
+ user: config.username,
44
+ password: config.password,
45
+ verify_ssl: config.verify_ssl)
46
+ end
47
+ # rubocop:enable MethodLength, LineLength
48
+
49
+ # Perform a REST GET operation against the specified endpoint
50
+ #
51
+ # @param [String] endpoint REST endpoint to use
52
+ # @param [Hash] headers Optional headers to use in request
53
+ # @return [String] JSON response body
54
+ def get(endpoint, headers = {})
55
+ headers = { accept: :json }.merge(headers)
56
+ @rest_resource[endpoint].get headers
57
+ end
58
+
59
+ # Perform a REST POST operation against the specified endpoint with the
60
+ # given data body
61
+ #
62
+ # @param [String] endpoint REST endpoint to use
63
+ # @param [String] body JSON data body to post
64
+ # @param [Hash] headers Optional headers to use in request
65
+ # @return [String] JSON response body
66
+ def post(endpoint, body, headers = {})
67
+ headers = { accept: :json, content_type: :json }.merge(headers)
68
+ @rest_resource[endpoint].post body, headers
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,5 @@
1
+ # VcoWorkflows
2
+ module VcoWorkflows
3
+ # Gem Version
4
+ VERSION = '0.2.3'.freeze
5
+ end
@@ -0,0 +1,395 @@
1
+ require_relative 'constants'
2
+ require_relative 'workflowservice'
3
+ require_relative 'workflowpresentation'
4
+ require_relative 'workflowtoken'
5
+ require_relative 'workflowparameter'
6
+ require 'json'
7
+
8
+ # VcoWorkflows
9
+ module VcoWorkflows
10
+ # rubocop:disable ClassLength
11
+
12
+ # Class to represent a Workflow as presented by vCenter Orchestrator.
13
+ class Workflow
14
+ # Workflow GUID
15
+ # @return [String] workflow GUID
16
+ attr_reader :id
17
+
18
+ # Workflow name
19
+ # @return [String] workflow name
20
+ attr_reader :name
21
+
22
+ # Workflow version
23
+ # @return [String] workflow version
24
+ attr_reader :version
25
+
26
+ # Workflow description
27
+ # @return [String] workflow description
28
+ attr_reader :description
29
+
30
+ # Workflow Input Parameters: Hash of WorkflowParameters, keyed by name
31
+ # @return [Hash<VcoWorkflows::WorkflowParameter>]
32
+ attr_reader :input_parameters
33
+
34
+ # Workflow Output Parameters: Hash of WorkflowParameters, keyed by name
35
+ # @return [Hash<VcoWorkflows::WorkflowParameter>]
36
+ attr_reader :output_parameters
37
+
38
+ # Workflow Service in use by this Workflow
39
+ # @return [VcoWorkflows::WorkflowService]
40
+ attr_accessor :service
41
+
42
+ # Workflow execution ID
43
+ # @return [String]
44
+ attr_reader :execution_id
45
+
46
+ # Workflow source JSON
47
+ # @return [String]
48
+ attr_reader :source_json
49
+
50
+ # rubocop:disable CyclomaticComplexity, PerceivedComplexity, MethodLength
51
+
52
+ # Create a Workflow object given vCenter Orchestrator's JSON description
53
+ #
54
+ # When passed `url`, `username` and `password` the necessary session and
55
+ # service objects will be created behind the scenes. Alternatively you can
56
+ # pass in a Config or a WorkflowService object if you have
57
+ # constructed them yourself. You may also pass in the path to a
58
+ # configuration file (`config_file`).
59
+ #
60
+ # @param [String] name Name of the requested workflow
61
+ # @param [Hash] options Hash of options:
62
+ # - id: (String) GUID for the Workflow
63
+ # - url: (String) vCO REST API URL
64
+ # - username: (String) User to authenticate as
65
+ # - password: (String) Password for username
66
+ # - verify_ssl: (Boolean) Perform TLS/SSL certificate validation
67
+ # - service: (VcoWorkflows::WorkflowService) WorkflowService to use for communicating to vCO
68
+ # - config: (VcoWorkflows::Config) Configuration object to use for this workflow's session
69
+ # - config_file: (String) Path to load configuration file from for this workflow's session
70
+ # @return [VcoWorkflows::Workflow]
71
+ def initialize(name = nil, options = {})
72
+ @options = {
73
+ id: nil,
74
+ url: nil,
75
+ username: nil,
76
+ password: nil,
77
+ verify_ssl: true,
78
+ service: nil,
79
+ config: nil,
80
+ config_file: nil
81
+ }.merge(options)
82
+
83
+ config = nil
84
+ @service = nil
85
+ @execution_id = nil
86
+
87
+ # -------------------------------------------------------------
88
+ # Figure out how to get a workflow service. If I can't, I die.
89
+ # (DUN dun dun...)
90
+
91
+ if options[:service]
92
+ @service = options[:service]
93
+ else
94
+ # If we were given a configuration object, use it
95
+ # If we were given a config file path, use it
96
+ # If we have a url, username and password, use them
97
+ # If all we have is a URL, try anyway, maybe we'll get username and
98
+ # password from ENV values (hey, it might work...)
99
+ if @options[:config]
100
+ config = @options[:config]
101
+ elsif @options[:config_file]
102
+ config = VcoWorkflows::Config.new(config_file: @options[:config_file])
103
+ elsif @options[:url] && @options[:username] && @options[:password]
104
+ config = VcoWorkflows::Config.new(url: @options[:url],
105
+ username: @options[:username],
106
+ password: @options[:password],
107
+ verify_ssl: @options[:verify_ssl])
108
+ elsif @options[:url]
109
+ config = VcoWorkflows::Config.new(url: @options[:url],
110
+ verify_ssl: @options[:verify_ssl])
111
+ end
112
+
113
+ # If we got a config object above, great. If it's still nil, VcoSession
114
+ # will accept that and try to load the default config file.
115
+ session = VcoWorkflows::VcoSession.new(config: config)
116
+ @service = VcoWorkflows::WorkflowService.new(session)
117
+ end
118
+
119
+ raise(IOError, 'Unable to create/use a WorkflowService!') if @service.nil?
120
+
121
+ # -------------------------------------------------------------
122
+ # Retrieve the workflow and parse it into a data structure
123
+ # If we're given both a name and ID, prefer the id
124
+ workflow_json = if @options[:id]
125
+ @service.get_workflow_for_id(@options[:id])
126
+ else
127
+ @service.get_workflow_for_name(name)
128
+ end
129
+ workflow_data = JSON.parse(workflow_json)
130
+
131
+ # Set up the attributes if they exist in the data json,
132
+ # otherwise nil them
133
+ @id = workflow_data.key?('id') ? workflow_data['id'] : nil
134
+ @name = workflow_data.key?('name') ? workflow_data['name'] : nil
135
+ @version = workflow_data.key?('version') ? workflow_data['version'] : nil
136
+ @description = workflow_data.key?('description') ? workflow_data['description'] : nil
137
+
138
+ # Process the input parameters
139
+ if workflow_data.key?('input-parameters')
140
+ @input_parameters = Workflow.parse_parameters(workflow_data['input-parameters'])
141
+ else
142
+ @input_parameters = {}
143
+ end
144
+
145
+ # Identify required input_parameters
146
+ wfpres = VcoWorkflows::WorkflowPresentation.new(@service, @id)
147
+ wfpres.required.each do |req_param|
148
+ @input_parameters[req_param].required(true)
149
+ end
150
+
151
+ # Process the output parameters
152
+ if workflow_data.key?('output-parameters')
153
+ @output_parameters = Workflow.parse_parameters(workflow_data['output-parameters'])
154
+ else
155
+ @output_parameters = {}
156
+ end
157
+ end
158
+ # rubocop:enable CyclomaticComplexity, PerceivedComplexity, MethodLength, LineLength
159
+
160
+ # vCO API URL used when creating this workflow
161
+ # @return [String]
162
+ def url
163
+ options[:url]
164
+ end
165
+
166
+ # vCO user name used when creating this workflow object
167
+ # @return [String]
168
+ def username
169
+ options[:username]
170
+ end
171
+
172
+ # vCO password used when creating this workflow object
173
+ # @return [String]
174
+ def password
175
+ options[:password]
176
+ end
177
+
178
+ # Verify SSL?
179
+ # @return [Boolean]
180
+ def verify_ssl?
181
+ options[:verify_ssl]
182
+ end
183
+
184
+ # rubocop:disable MethodLength
185
+
186
+ # Parse json parameters and return a nice hash
187
+ # @param [Array<Hash>] parameter_data Array of parameter data hashes
188
+ # by vCO
189
+ # @return [Hash]
190
+ def self.parse_parameters(parameter_data = [])
191
+ wfparams = {}
192
+ parameter_data.each do |parameter|
193
+ wfparam = VcoWorkflows::WorkflowParameter.new(parameter['name'], parameter['type'])
194
+ if parameter['value']
195
+ if wfparam.type.eql?('Array')
196
+ value = []
197
+ begin
198
+ parameter['value'][wfparam.type.downcase]['elements'].each do |element|
199
+ value << element[element.keys.first]['value']
200
+ end
201
+ rescue StandardError => error
202
+ parse_failure(error, wfparam, parameter)
203
+ end
204
+ else
205
+ begin
206
+ value = parameter['value'][parameter['value'].keys.first]['value']
207
+ rescue StandardError => error
208
+ parse_failure(error, wfparam, parameter)
209
+ end
210
+ end
211
+ value = nil if value.eql?('null')
212
+ wfparam.set(value)
213
+ end
214
+ wfparams[parameter['name']] = wfparam
215
+ end
216
+ wfparams
217
+ end
218
+ # rubocop:enable MethodLength
219
+
220
+ # Process exceptions raised in parse_parameters by bravely ignoring them
221
+ # and forging ahead blindly!
222
+ # @param [Exception] error
223
+ def self.parse_failure(error, wfparam, parameter)
224
+ $stderr.puts "\nWhoops!"
225
+ $stderr.puts "Ran into a problem parsing parameter #{wfparam.name} (#{wfparam.type})!"
226
+ $stderr.puts "Source data: #{JSON.pretty_generate(parameter)}\n"
227
+ $stderr.puts error.message
228
+ $stderr.puts "\nBravely forging on and ignoring parameter #{wfparam.name}!"
229
+ end
230
+
231
+ # Get an array of the names of all the required input parameters
232
+ # @return [Hash] Hash of WorkflowParameter input parameters which
233
+ # are required for this workflow
234
+ def required_parameters
235
+ required = {}
236
+ @input_parameters.each_value { |v| required[v.name] = v if v.required? }
237
+ required
238
+ end
239
+
240
+ # Get the parameter object named. If a value is provided, set the value
241
+ # and return the parameter object.
242
+ #
243
+ # To get a parameter value, use parameter(parameter_name).value
244
+ #
245
+ # @param [String] parameter_name Name of the parameter to get
246
+ # @param [Object, nil] parameter_value Optional value for parameter.
247
+ # @return [VcoWorkflows::WorkflowParameter] The resulting WorkflowParameter
248
+ #
249
+ # rubocop:disable Metrics/MethodLength
250
+ def parameter(parameter_name, parameter_value = nil)
251
+ unless parameter_value.nil?
252
+ if @input_parameters.key?(parameter_name)
253
+ @input_parameters[parameter_name].set parameter_value
254
+ else
255
+ $stderr.puts "\nAttempted to set a value for a non-existent WorkflowParameter!"
256
+ $stderr.puts "It appears that there is no parameter \"#{parameter}\"."
257
+ $stderr.puts "Valid parameter names are: #{@input_parameters.keys.join(', ')}"
258
+ $stderr.puts ''
259
+ raise(IOError, ERR[:no_such_parameter])
260
+ end
261
+ end
262
+ @input_parameters[parameter_name]
263
+ end
264
+ # rubocop:enable MethodLength
265
+
266
+ # Set a parameter with a WorkflowParameter object
267
+ # @param [VcoWorkflows::WorkflowParameter] wfparameter New parameter
268
+ def parameter=(wfparameter)
269
+ @input_parameters[wfparameter.name] = wfparameter
270
+ end
271
+
272
+ # Determine whether a parameter has been set
273
+ # @param [String] parameter_name Name of the parameter to check
274
+ # @return [Boolean]
275
+ def parameter?(parameter_name)
276
+ parameter(parameter_name).set?
277
+ end
278
+
279
+ # Set all input parameters using the given hash
280
+ # @param [Hash] parameter_hash input parameter values keyed by
281
+ # input_parameter name
282
+ def parameters=(parameter_hash)
283
+ parameter_hash.each { |name, value| parameter(name, value) }
284
+ end
285
+
286
+ # Set a parameter to a value.
287
+ # @deprecated Use {#parameter} instead
288
+ # @param [String] parameter_name name of the parameter to set
289
+ # @param [Object] value value to set
290
+ # @return [VcoWorkflows::WorkflowParameter] The resulting WorkflowParameter
291
+ def set_parameter(parameter_name, value)
292
+ parameter(parameter_name, value)
293
+ end
294
+
295
+ # Get the value for an input parameter
296
+ # @deprecated Use {#parameter} to retrieve the
297
+ # {VcoWorkflows::WorkflowParameter} object, instead
298
+ # @param [String] parameter_name Name of the input parameter
299
+ # whose value to get
300
+ # @return [Object]
301
+ def get_parameter(parameter_name)
302
+ parameter(parameter_name).value
303
+ end
304
+
305
+ # Execute this workflow
306
+ # @param [VcoWorkflows::WorkflowService] workflow_service
307
+ # @return [String] Workflow Execution ID
308
+ def execute(workflow_service = nil)
309
+ # If we're not given an explicit workflow service for this execution
310
+ # request, use the one defined when we were created.
311
+ workflow_service = @service if workflow_service.nil?
312
+ # If we still have a nil workflow_service, go home.
313
+ raise(IOError, ERR[:no_workflow_service_defined]) if workflow_service.nil?
314
+ # Make sure we didn't forget any required parameters
315
+ verify_parameters
316
+ # Let's get this thing running!
317
+ @execution_id = workflow_service.execute_workflow(@id, input_parameter_json)
318
+ end
319
+
320
+ # Get a list of all the executions of this workflow. Wrapper for
321
+ # VcoWorkflows::WorkflowService#get_execution_list
322
+ # @return [Hash]
323
+ def executions
324
+ @service.get_execution_list(@id)
325
+ end
326
+
327
+ # Return a WorkflowToken
328
+ # @param [String] execution_id optional execution id to get logs for
329
+ # @return [VcoWorkflows::WorkflowToken]
330
+ def token(execution_id = nil)
331
+ execution_id = @execution_id if execution_id.nil?
332
+ VcoWorkflows::WorkflowToken.new(@service, @id, execution_id)
333
+ end
334
+
335
+ # Return logs for the given execution
336
+ # @param [String] execution_id optional execution id to get logs for
337
+ # @return [VcoWorkflows::WorkflowExecutionLog]
338
+ def log(execution_id = nil)
339
+ execution_id = @execution_id if execution_id.nil?
340
+ log_json = @service.get_log(@id, execution_id)
341
+ VcoWorkflows::WorkflowExecutionLog.new(log_json)
342
+ end
343
+
344
+ # rubocop:disable MethodLength
345
+
346
+ # Stringify the workflow
347
+ # @return [String]
348
+ def to_s
349
+ string = "Workflow: #{@name}\n"
350
+ string << "ID: #{@id}\n"
351
+ string << "Description: #{@description}\n"
352
+ string << "Version: #{@version}\n"
353
+
354
+ string << "\nInput Parameters:\n"
355
+ unless @input_parameters.empty?
356
+ @input_parameters.each_value { |wf_param| string << " #{wf_param}" }
357
+ end
358
+
359
+ string << "\nOutput Parameters:" << "\n"
360
+ unless @output_parameters.empty?
361
+ @output_parameters.each_value { |wf_param| string << " #{wf_param}" }
362
+ end
363
+
364
+ # Assert
365
+ string
366
+ end
367
+ # rubocop:enable MethodLength
368
+
369
+ private
370
+
371
+ # Convert the input parameters to a JSON document
372
+ # @return [String]
373
+ def input_parameter_json
374
+ tmp_params = []
375
+ @input_parameters.each_value { |v| tmp_params << v.as_struct if v.set? }
376
+ param_struct = { parameters: tmp_params }
377
+ param_struct.to_json
378
+ end
379
+
380
+ # Verify that all mandatory input parameters have values
381
+ def verify_parameters
382
+ required_parameters.each do |name, wfparam|
383
+ # rubocop:disable Style/Next
384
+ if wfparam.required? && (wfparam.value.nil? || wfparam.value.to_s.empty?)
385
+ raise(
386
+ IOError,
387
+ ERR[:param_verify_failed] << "#{name} required but not present."
388
+ )
389
+ end
390
+ # rubocop:enable Style/Next
391
+ end
392
+ end
393
+ end
394
+ # rubocop:enable ClassLength
395
+ end