vmware-vra 2.7.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/CHANGELOG.md +5 -0
  4. data/README.md +79 -144
  5. data/Rakefile +0 -11
  6. data/lib/vra/catalog.rb +39 -8
  7. data/lib/vra/catalog_base.rb +62 -0
  8. data/lib/vra/catalog_item.rb +28 -74
  9. data/lib/vra/catalog_source.rb +116 -0
  10. data/lib/vra/catalog_type.rb +56 -0
  11. data/lib/vra/client.rb +62 -54
  12. data/lib/vra/deployment.rb +155 -0
  13. data/lib/vra/deployment_request.rb +117 -0
  14. data/lib/vra/{resources.rb → deployments.rb} +26 -17
  15. data/lib/vra/exceptions.rb +1 -1
  16. data/lib/vra/http.rb +11 -6
  17. data/lib/vra/request.rb +28 -36
  18. data/lib/vra/request_parameters.rb +12 -12
  19. data/lib/vra/resource.rb +32 -203
  20. data/lib/vra/version.rb +2 -2
  21. data/lib/vra.rb +15 -12
  22. data/spec/catalog_item_spec.rb +64 -222
  23. data/spec/catalog_source_spec.rb +178 -0
  24. data/spec/catalog_spec.rb +112 -72
  25. data/spec/catalog_type_spec.rb +114 -0
  26. data/spec/client_spec.rb +271 -226
  27. data/spec/deployment_request_spec.rb +192 -0
  28. data/spec/deployment_spec.rb +227 -0
  29. data/spec/deployments_spec.rb +80 -0
  30. data/spec/fixtures/resource/sample_catalog_item.json +18 -0
  31. data/spec/fixtures/resource/sample_catalog_item_2.json +18 -0
  32. data/spec/fixtures/resource/sample_catalog_source.json +20 -0
  33. data/spec/fixtures/resource/sample_catalog_type.json +49 -0
  34. data/spec/fixtures/resource/sample_dep_actions.json +58 -0
  35. data/spec/fixtures/resource/sample_dep_request.json +19 -0
  36. data/spec/fixtures/resource/sample_dep_resource.json +112 -0
  37. data/spec/fixtures/resource/sample_deployment.json +26 -0
  38. data/spec/fixtures/resource/sample_entitlements.json +25 -0
  39. data/spec/http_spec.rb +63 -61
  40. data/spec/request_spec.rb +62 -68
  41. data/spec/resource_spec.rb +71 -390
  42. data/spec/spec_helper.rb +10 -4
  43. data/vmware-vra.gemspec +0 -1
  44. metadata +40 -30
  45. data/.travis.yml +0 -14
  46. data/lib/vra/catalog_request.rb +0 -137
  47. data/lib/vra/requests.rb +0 -41
  48. data/spec/catalog_request_spec.rb +0 -268
  49. data/spec/requests_spec.rb +0 -60
  50. data/spec/resources_spec.rb +0 -71
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Ashique Saidalavi (<ashique.saidalavi@progress.com>)
4
+ # Copyright:: Copyright (c) 2022 Chef Software, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ require 'ffi_yajl' unless defined?(FFI_Yajl)
20
+
21
+ module Vra
22
+ # Class that represents the Catalog Source
23
+ class CatalogSource < Vra::CatalogBase
24
+ INDEX_URL = '/catalog/api/admin/sources'
25
+
26
+ # @param client [Vra::Client] - a vra client object
27
+ # @param opts [Hash] - Contains the either id of the catalog or the data hash
28
+ # Either one of id or data hash is required, must not supply both
29
+ def initialize(client, opts)
30
+ super
31
+ validate!
32
+ fetch_data
33
+ end
34
+
35
+ def name
36
+ data['name']
37
+ end
38
+
39
+ def catalog_type_id
40
+ data['typeId']
41
+ end
42
+
43
+ def catalog_type
44
+ @catalog_type ||= Vra::CatalogType.new(client, id: catalog_type_id)
45
+ end
46
+
47
+ def config
48
+ data['config']
49
+ end
50
+
51
+ def global?
52
+ data['global'] == true
53
+ end
54
+
55
+ def project_id
56
+ config['sourceProjectId']
57
+ end
58
+
59
+ def entitle!(opts = {})
60
+ super(opts.merge(type: 'CatalogSourceIdentifier'))
61
+ end
62
+
63
+ class << self
64
+ # Method to create a catalog source
65
+ def create(client, opts)
66
+ validate_create!(opts)
67
+
68
+ response = client.http_post(
69
+ '/catalog/api/admin/sources',
70
+ FFI_Yajl::Encoder.encode(create_params(opts)),
71
+ opts[:skip_auth] || false
72
+ )
73
+
74
+ return false unless response.success?
75
+
76
+ new(client, data: FFI_Yajl::Parser.parse(response.body))
77
+ end
78
+
79
+ def entitle!(client, id)
80
+ new(client, id: id).entitle!
81
+ end
82
+
83
+ private
84
+
85
+ def validate_create!(opts)
86
+ %i[name catalog_type_id project_id].each do |arg|
87
+ raise ArgumentError, "#{arg} param is required to perform the create action" unless opts.key?(arg)
88
+ end
89
+ end
90
+
91
+ def create_params(opts)
92
+ {
93
+ 'name': opts[:name],
94
+ 'typeId': opts[:catalog_type_id],
95
+ 'config': {
96
+ 'sourceProjectId': opts[:project_id]
97
+ }
98
+ }
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def fetch_data
105
+ fetch_catalog_data && return if data.nil?
106
+
107
+ @id = data['id']
108
+ end
109
+
110
+ def fetch_catalog_data
111
+ @data = client.get_parsed("/catalog/api/admin/sources/#{id}")
112
+ rescue Vra::Exception::HTTPNotFound
113
+ raise Vra::Exception::NotFound, "catalog source ID #{id} does not exist"
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Ashique Saidalavi (<ashique.saidalavi@progress.com>)
4
+ # Copyright:: Copyright (c) 2022 Chef Software, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ module Vra
20
+ # Class that represents the Catalog Type
21
+ class CatalogType < Vra::CatalogBase
22
+ INDEX_URL = '/catalog/api/types'
23
+
24
+ def initialize(client, opts = {})
25
+ super
26
+ validate!
27
+ fetch_data
28
+ end
29
+
30
+ def name
31
+ data['name']
32
+ end
33
+
34
+ def base_url
35
+ data['baseUri']
36
+ end
37
+
38
+ def config_schema
39
+ data['configSchema']
40
+ end
41
+
42
+ def icon_id
43
+ data['iconId']
44
+ end
45
+
46
+ private
47
+
48
+ def fetch_data
49
+ @id = data['id'] and return unless data.nil?
50
+
51
+ @data = client.get_parsed("/catalog/api/types/#{id}")
52
+ rescue Vra::Exception::HTTPNotFound
53
+ raise Vra::Exception::NotFound, "catalog type ID #{id} does not exist"
54
+ end
55
+ end
56
+ end
data/lib/vra/client.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # Author:: Chef Partner Engineering (<partnereng@chef.io>)
4
- # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # Copyright:: Copyright (c) 2022 Chef Software, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,22 +17,26 @@
17
17
  # limitations under the License.
18
18
  #
19
19
 
20
- require "ffi_yajl" unless defined?(FFI_Yajl)
21
- require "passwordmasker"
22
- require "vra/http"
20
+ require 'ffi_yajl' unless defined?(FFI_Yajl)
21
+ require 'passwordmasker'
22
+ require 'vra/http'
23
23
 
24
24
  module Vra
25
25
  class Client
26
+ ACCESS_TOKEN_URL = '/csp/gateway/am/api/login?access_token'
27
+ ROLES_URL = '/csp/gateway/am/api/loggedin/user/orgs'
28
+
26
29
  attr_accessor :page_size
27
30
 
28
31
  def initialize(opts)
29
- @base_url = opts[:base_url]
30
- @username = opts[:username]
31
- @password = PasswordMasker.new(opts[:password])
32
- @tenant = opts[:tenant]
33
- @verify_ssl = opts.fetch(:verify_ssl, true)
34
- @bearer_token = PasswordMasker.new(nil)
35
- @page_size = opts.fetch(:page_size, 20)
32
+ @base_url = opts[:base_url]
33
+ @username = opts[:username]
34
+ @password = PasswordMasker.new(opts[:password])
35
+ @tenant = opts[:tenant]
36
+ @verify_ssl = opts.fetch(:verify_ssl, true)
37
+ @refresh_token = PasswordMasker.new(nil)
38
+ @access_token = PasswordMasker.new(nil)
39
+ @page_size = opts.fetch(:page_size, 20)
36
40
 
37
41
  validate_client_options!
38
42
  end
@@ -43,15 +47,11 @@ module Vra
43
47
  #
44
48
 
45
49
  def catalog
46
- Vra::Catalog.new(self)
47
- end
48
-
49
- def requests(*args)
50
- Vra::Requests.new(self, *args)
50
+ @catalog ||= Vra::Catalog.new(self)
51
51
  end
52
52
 
53
- def resources(*args)
54
- Vra::Resources.new(self, *args)
53
+ def deployments
54
+ @deployments ||= Vra::Deployments.new(self)
55
55
  end
56
56
 
57
57
  #########################
@@ -59,55 +59,64 @@ module Vra
59
59
  # client methods
60
60
  #
61
61
 
62
- def bearer_token
63
- @bearer_token.value
62
+ def access_token
63
+ @access_token.value
64
+ end
65
+
66
+ def refresh_token
67
+ @refresh_token.value
68
+ end
69
+
70
+ def access_token=(value)
71
+ @access_token.value = value
64
72
  end
65
73
 
66
- def bearer_token=(value)
67
- @bearer_token.value = value
74
+ def refresh_token=(value)
75
+ @refresh_token.value = value
68
76
  end
69
77
 
70
- def bearer_token_request_body
78
+ def token_params
71
79
  {
72
- "username" => @username,
73
- "password" => @password.value,
74
- "tenant" => @tenant,
80
+ 'username': @username,
81
+ 'password': @password.value,
82
+ 'tenant': @tenant
75
83
  }
76
84
  end
77
85
 
78
86
  def request_headers
79
- headers = {}
80
- headers["Accept"] = "application/json"
81
- headers["Content-Type"] = "application/json"
82
- headers["Authorization"] = "Bearer #{@bearer_token.value}" unless @bearer_token.value.nil?
87
+ headers = {}
88
+ headers['Accept'] = 'application/json'
89
+ headers['Content-Type'] = 'application/json'
90
+ headers['csp-auth-token'] = @access_token.value unless @access_token.value.nil?
91
+
83
92
  headers
84
93
  end
85
94
 
86
95
  def authorize!
87
- generate_bearer_token unless authorized?
96
+ generate_access_token unless authorized?
88
97
 
89
- raise Vra::Exception::Unauthorized, "Unable to authorize against vRA" unless authorized?
98
+ raise Vra::Exception::Unauthorized, 'Unable to authorize against vRA' unless authorized?
90
99
  end
91
100
 
92
101
  def authorized?
93
- return false if @bearer_token.value.nil?
102
+ return false if @access_token.value.nil?
94
103
 
95
- response = http_head("/identity/api/tokens/#{@bearer_token.value}", :skip_auth)
96
- response.success_no_content?
104
+ response = http_head(ROLES_URL, :skip_auth)
105
+ response.success?
97
106
  end
98
107
 
99
- def generate_bearer_token
100
- @bearer_token.value = nil
108
+ def generate_access_token
109
+ @access_token.value = nil
101
110
  validate_client_options!
102
111
 
103
- response = http_post("/identity/api/tokens",
104
- FFI_Yajl::Encoder.encode(bearer_token_request_body),
112
+ response = http_post(ACCESS_TOKEN_URL,
113
+ FFI_Yajl::Encoder.encode(token_params),
105
114
  :skip_auth)
106
- unless response.success_ok?
107
- raise Vra::Exception::Unauthorized, "Unable to get bearer token: #{response.body}"
108
- end
115
+ raise Vra::Exception::Unauthorized, "Unable to get the access token: #{response.body}" unless response.success_ok?
109
116
 
110
- @bearer_token.value = FFI_Yajl::Parser.parse(response.body)["id"]
117
+ response_body = FFI_Yajl::Parser.parse(response.body)
118
+ @access_token.value = response_body['access_token']
119
+ @refresh_token.value = response_body['refresh_token']
111
120
  end
112
121
 
113
122
  def full_url(path)
@@ -140,22 +149,26 @@ module Vra
140
149
  response.body
141
150
  end
142
151
 
152
+ def http_delete(path, skip_auth = nil)
153
+ http_fetch(:delete, path, skip_auth)
154
+ end
155
+
143
156
  def get_parsed(path)
144
157
  FFI_Yajl::Parser.parse(http_get!(path))
145
158
  end
146
159
 
147
- def http_get_paginated_array!(path)
160
+ def http_get_paginated_array!(path, filter = nil)
148
161
  items = []
149
- page = 1
150
- base_path = path + "?limit=#{page_size}"
162
+ page = 0
163
+ base_path = path + "?$top=#{page_size}"
164
+ base_path += "&#{filter}" if filter
151
165
 
152
166
  loop do
153
- response = get_parsed("#{base_path}&page=#{page}")
167
+ response = get_parsed("#{base_path}&$skip=#{page * page_size}")
154
168
  items += response["content"]
155
169
 
156
- break if page >= response["metadata"]["totalPages"]
157
-
158
170
  page += 1
171
+ break if page >= response["totalPages"]
159
172
  end
160
173
 
161
174
  if items.uniq!
@@ -219,10 +232,5 @@ module Vra
219
232
  rescue URI::InvalidURIError
220
233
  false
221
234
  end
222
-
223
- def fetch_subtenant_items(tenant, subtenant_name)
224
- http_get("/identity/api/tenants/#{tenant}/subtenants?%24filter=name+eq+'#{subtenant_name}'")
225
- end
226
-
227
235
  end
228
236
  end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Ashique Saidalavi (<ashique.saidalavi@progress.com>)
4
+ # Copyright:: Copyright (c) 2022 Chef Software, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require "ffi_yajl" unless defined?(FFI_Yajl)
21
+
22
+ module Vra
23
+ # Class that represents the Deployment Object
24
+ class Deployment
25
+ INDEX_URL = '/deployment/api/deployments'
26
+
27
+ attr_reader :id
28
+
29
+ def initialize(client, opts = {})
30
+ @client = client
31
+ @id = opts[:id]
32
+ @data = opts[:data]
33
+ validate!
34
+
35
+ if @data.nil?
36
+ refresh
37
+ elsif @id.nil?
38
+ @id = @data['id']
39
+ end
40
+ end
41
+
42
+ def name
43
+ @data['name']
44
+ end
45
+
46
+ def description
47
+ @data['description']
48
+ end
49
+
50
+ def org_id
51
+ @data['orgId']
52
+ end
53
+
54
+ def blueprint_id
55
+ @data['blueprintId']
56
+ end
57
+
58
+ def owner
59
+ @data['ownedBy']
60
+ end
61
+
62
+ def status
63
+ @data['status']
64
+ end
65
+
66
+ def successful?
67
+ status == 'CREATE_SUCCESSFUL'
68
+ end
69
+
70
+ def failed?
71
+ status == 'CREATE_FAILED'
72
+ end
73
+
74
+ def completed?
75
+ successful? || failed?
76
+ end
77
+
78
+ def actions
79
+ @actions = client.get_parsed("/deployment/api/deployments/#{id}/actions")
80
+ end
81
+
82
+ def action_id_by_name(action_name)
83
+ action = actions.find { |x| x['name'] == action_name}
84
+ return if action.nil?
85
+
86
+ action['id']
87
+ end
88
+
89
+ def resources
90
+ response = client.get_parsed("/deployment/api/deployments/#{id}/resources")
91
+
92
+ response['content'].map! { |x| Vra::Resource.new(client, id, data: x) }
93
+ end
94
+
95
+ def resource_by_id(res_id)
96
+ Vra::Resource.new(client, id, id: res_id)
97
+ end
98
+
99
+ def requests
100
+ response = client.get_parsed("/deployment/api/deployments/#{id}/requests")
101
+
102
+ response['content'].map! { |x| Vra::Request.new(client, id, id: x['id'], data: x) }
103
+ end
104
+
105
+ def refresh
106
+ @data = client.get_parsed("/deployment/api/deployments/#{id}")
107
+ rescue Vra::Exception::HTTPNotFound
108
+ raise Vra::Exception::NotFound, "deployment with ID #{id} does not exist"
109
+ end
110
+
111
+ def destroy(reason = '')
112
+ action_id = action_id_by_name('Delete')
113
+ raise Vra::Exception::NotFound, "No destroy action found for resource #{@id}" if action_id.nil?
114
+
115
+ submit_action_request(action_id, reason)
116
+ end
117
+
118
+ def power_off(reason = '')
119
+ action_id = action_id_by_name('PowerOff')
120
+ raise Vra::Exception::NotFound, "No power-off action found for resource #{@id}" if action_id.nil?
121
+
122
+ submit_action_request(action_id, reason)
123
+ end
124
+
125
+ def power_on(reason = nil)
126
+ action_id = action_id_by_name('PowerOn')
127
+ raise Vra::Exception::NotFound, "No power-on action found for resource #{@id}" if action_id.nil?
128
+
129
+ submit_action_request(action_id, reason)
130
+ end
131
+
132
+ private
133
+
134
+ attr_reader :client, :data
135
+
136
+ def validate!
137
+ raise ArgumentError, 'must supply id or data hash' if @id.nil? && @data.nil?
138
+ end
139
+
140
+ def submit_action_request(action_id, reason)
141
+ response = client.http_post!("/deployment/api/deployments/#{id}/requests",
142
+ FFI_Yajl::Encoder.encode(submit_action_payload(action_id, reason)))
143
+
144
+ Vra::Request.new(client, id, data: FFI_Yajl::Parser.parse(response))
145
+ end
146
+
147
+ def submit_action_payload(action_id, reason)
148
+ {
149
+ "actionId": action_id,
150
+ "inputs": {},
151
+ "reason": reason
152
+ }
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Author:: Ashique Saidalavi (<ashique.saidalavi@progress.com>)
4
+ # Copyright:: Copyright (c) 2022 Chef Software, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ require 'ffi_yajl' unless defined?(FFI_Yajl)
20
+
21
+ # Overriding the hash object to add the deep_merge method
22
+ class ::Hash
23
+ def deep_merge(second)
24
+ merger = proc { |_key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
25
+ merge(second, &merger)
26
+ end
27
+ end
28
+
29
+ module Vra
30
+ # class that handles the deployment request with catalog
31
+ class DeploymentRequest
32
+ attr_reader :catalog_id
33
+ attr_accessor :image_mapping, :name, :flavor_mapping,
34
+ :project_id, :version, :count
35
+
36
+ def initialize(client, catalog_id, opts = {})
37
+ @client = client
38
+ @catalog_id = catalog_id
39
+ @image_mapping = opts[:image_mapping]
40
+ @name = opts[:name]
41
+ @flavor_mapping = opts[:flavor_mapping]
42
+ @project_id = opts[:project_id]
43
+ @version = opts[:version]
44
+ @count = opts[:count] || 1
45
+ @additional_params = opts[:additional_params] || Vra::RequestParameters.new
46
+ end
47
+
48
+ def submit
49
+ validate!
50
+ begin
51
+ response = send_request!
52
+ rescue Vra::Exception::HTTPError => e
53
+ raise Vra::Exception::RequestError, "Unable to submit request: #{e.message}, trace: #{e.errors.join(', ')}"
54
+ rescue StandardError => e
55
+ raise e, e.message
56
+ end
57
+
58
+ request_id = FFI_Yajl::Parser.parse(response)[0]['deploymentId']
59
+ Vra::Deployment.new(client, id: request_id)
60
+ end
61
+
62
+ def set_parameter(key, type, value)
63
+ @additional_params.set(key, type, value)
64
+ end
65
+
66
+ def set_parameters(key, value_data)
67
+ @additional_params.set_parameters(key, value_data)
68
+ end
69
+
70
+ def delete_parameter(key)
71
+ @additional_params.delete(key)
72
+ end
73
+
74
+ def parameters
75
+ @additional_params.to_vra
76
+ end
77
+
78
+ def hash_parameters
79
+ @additional_params.to_h
80
+ end
81
+
82
+ private
83
+
84
+ attr_reader :client
85
+
86
+ def validate!
87
+ missing_params = []
88
+ %i[image_mapping flavor_mapping name project_id version].each do |arg|
89
+ missing_params << arg if send(arg).nil?
90
+ end
91
+
92
+ return if missing_params.empty?
93
+
94
+ raise ArgumentError, "Unable to submit request, required param(s) missing => #{missing_params.join(', ')}"
95
+ end
96
+
97
+ def send_request!
98
+ client.http_post!(
99
+ "/catalog/api/items/#{catalog_id}/request",
100
+ FFI_Yajl::Encoder.encode(request_payload)
101
+ )
102
+ end
103
+
104
+ def request_payload
105
+ {
106
+ 'deploymentName': name,
107
+ 'projectId': project_id,
108
+ 'version': version,
109
+ 'inputs': {
110
+ 'count': count,
111
+ 'image': image_mapping,
112
+ 'flavor': flavor_mapping
113
+ }
114
+ }.deep_merge(parameters)
115
+ end
116
+ end
117
+ end