kitchen-oci 1.15.1 → 1.16.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,17 +17,15 @@
17
17
  # See the License for the specific language governing permissions and
18
18
  # limitations under the License.
19
19
 
20
- # rubocop:disable Metrics/AbcSize
21
-
22
20
  # This require fixes bug in ChefDK 4.0.60-1 on Linux.
23
- require 'forwardable'
24
- require 'base64'
25
- require 'erb'
26
- require 'kitchen'
27
- require 'oci'
28
- require 'openssl'
29
- require 'uri'
30
- require 'zlib'
21
+ require "forwardable" unless defined?(Forwardable)
22
+ require "base64" unless defined?(Base64)
23
+ require "erb" unless defined?(Erb)
24
+ require "kitchen"
25
+ require "oci"
26
+ require "openssl" unless defined?(OpenSSL)
27
+ require "uri" unless defined?(URI)
28
+ require "zlib" unless defined?(Zlib)
31
29
 
32
30
  module Kitchen
33
31
  module Driver
@@ -35,677 +33,133 @@ module Kitchen
35
33
  #
36
34
  # @author Stephen Pearson <stephen.pearson@oracle.com>
37
35
  class Oci < Kitchen::Driver::Base # rubocop:disable Metrics/ClassLength
36
+ require_relative "oci_version"
37
+ require_relative "oci/models"
38
+
39
+ plugin_version Kitchen::Driver::OCI_VERSION
40
+
38
41
  # required config items
39
42
  required_config :availability_domain
40
43
  required_config :shape
41
44
  required_config :subnet_id
42
45
 
43
46
  # common config items
47
+ default_config :oci_config, {}
48
+ default_config :oci_config_file, nil
49
+ default_config :oci_profile_name, nil
44
50
  default_config :compartment_id, nil
45
51
  default_config :compartment_name, nil
46
- default_config :instance_type, 'compute'
47
- default_config :hostname_prefix, nil
48
- default_keypath = File.expand_path(File.join(%w[~ .ssh id_rsa.pub]))
52
+ default_config :instance_type, "compute"
53
+ default_config :image_id
54
+ default_config :hostname_prefix do |hnp|
55
+ hnp.instance.name
56
+ end
57
+ default_keypath = File.expand_path(File.join(%w{~ .ssh id_rsa.pub}))
49
58
  default_config :ssh_keypath, default_keypath
50
59
  default_config :post_create_script, nil
51
60
  default_config :proxy_url, nil
52
61
  default_config :user_data, nil
53
62
  default_config :freeform_tags, {}
54
63
  default_config :defined_tags, {}
55
-
56
- # compute config items
57
- default_config :image_id
58
- default_config :boot_volume_size_in_gbs, nil
59
- default_config :use_private_ip, false
60
- default_config :oci_config, {}
61
- default_config :oci_config_file, nil
62
- default_config :oci_profile_name, nil
63
- default_config :setup_winrm, false
64
- default_config :winrm_user, 'opc'
65
- default_config :winrm_password, nil
64
+ default_config :custom_metadata, {}
66
65
  default_config :use_instance_principals, false
67
66
  default_config :use_token_auth, false
68
- default_config :preemptible_instance, false
69
67
  default_config :shape_config, {}
70
- default_config :custom_metadata, {}
71
-
72
- # dbaas config items
73
- default_config :dbaas, {}
68
+ default_config :nsg_ids, []
74
69
 
75
- # blockstorage config items
70
+ # compute only configs
71
+ default_config :setup_winrm, false
72
+ default_config :winrm_user, "opc"
73
+ default_config :winrm_password, nil
74
+ default_config :preemptible_instance, false
75
+ default_config :boot_volume_size_in_gbs, nil
76
+ default_config :use_private_ip, false
76
77
  default_config :volumes, {}
77
78
 
78
- def create(state)
79
- return if state[:server_id]
80
-
81
- state = process_windows_options(state)
82
-
83
- instance_id = launch_instance(state)
84
-
85
- state[:server_id] = instance_id
86
- state[:hostname] = instance_ip(instance_id)
87
-
88
- instance.transport.connection(state).wait_until_ready
89
-
90
- state[:volumes] = process_volumes_list(state)
91
- state[:volume_attachments] = process_volume_attachments(state)
92
-
93
- return unless config[:post_create_script]
94
-
95
- info('Running post create script')
96
- script = config[:post_create_script]
97
- instance.transport.connection(state).execute(script)
98
- end
99
-
100
- def destroy(state)
101
- return unless state[:server_id]
102
-
103
- instance.transport.connection(state).close
104
-
105
- if state[:volume_attachments]
106
- state[:volume_attachments].each do |attachment|
107
- volume_detach(attachment)
108
- end
109
- end
110
-
111
- if state[:volumes]
112
- state[:volumes].each do |vol|
113
- volume_delete(vol[:id])
114
- end
115
- end
116
-
117
- if instance_type == 'compute'
118
- comp_api.terminate_instance(state[:server_id])
119
- elsif instance_type == 'dbaas'
120
- dbaas_api.terminate_db_system(state[:server_id])
121
- end
122
-
123
- state.delete(:server_id)
124
- state.delete(:hostname)
125
- end
126
-
127
- def process_freeform_tags(freeform_tags)
128
- prov = instance.provisioner.instance_variable_get(:@config)
129
- tags = %w[run_list policyfile]
130
- tags.each do |tag|
131
- freeform_tags[tag] = prov[tag.to_sym].join(',') unless prov[tag.to_sym].nil? || prov[tag.to_sym].empty?
132
- end
133
- freeform_tags[:kitchen] = true
134
- freeform_tags
135
- end
136
-
137
- def process_windows_options(state)
138
- state[:username] = config[:winrm_user] if config[:setup_winrm]
139
- if config[:setup_winrm] == true &&
140
- config[:password].nil? &&
141
- state[:password].nil?
142
- state[:password] = config[:winrm_password] || random_password
143
- end
144
- state
145
- end
146
-
147
- private
148
-
149
- def compartment_id
150
- return config[:compartment_id] if config[:compartment_id]
151
- raise 'must specify either compartment_id or compartment_name' unless config[:compartment_name]
152
- ident_api.list_compartments(tenancy).data.find do |item|
153
- return item.id if item.name == config[:compartment_name]
154
- end
155
- raise 'compartment not found'
156
- end
157
-
158
- def tenancy
159
- if config[:use_instance_principals]
160
- sign = OCI::Auth::Signers::InstancePrincipalsSecurityTokenSigner.new
161
- sign.instance_variable_get '@tenancy_id'
162
- else
163
- oci_config.tenancy
164
- end
165
- end
166
-
167
- def instance_type
168
- raise 'instance_type must be either compute or dbaas!' unless %w[compute dbaas].include?(config[:instance_type].downcase)
169
-
170
- config[:instance_type].downcase
171
- end
172
-
173
- ####################
174
- # OCI config setup #
175
- ####################
176
- def oci_config
177
- # OCI::Config is missing this
178
- OCI::Config.class_eval { attr_accessor :security_token_file } if config[:use_token_auth]
179
-
180
- opts = {}
181
- opts[:config_file_location] = config[:oci_config_file] if config[:oci_config_file]
182
- opts[:profile_name] = config[:oci_profile_name] if config[:oci_profile_name]
183
-
184
- oci_config = begin
185
- OCI::ConfigFileLoader.load_config(**opts)
186
- rescue OCI::ConfigFileLoader::Errors::ConfigFileNotFoundError
187
- OCI::Config.new
188
- end
189
-
190
- config[:oci_config].each do |key, value|
191
- oci_config.send("#{key}=", value) unless value.nil? || value.empty?
192
- end
193
- oci_config
194
- end
195
-
196
- def proxy_config
197
- if config[:proxy_url]
198
- URI.parse(config[:proxy_url])
199
- else
200
- URI.parse('http://').find_proxy
201
- end
202
- end
203
-
204
- def api_proxy
205
- prx = proxy_config
206
- return nil unless prx
207
-
208
- if prx.user
209
- OCI::ApiClientProxySettings.new(prx.host, prx.port, prx.user,
210
- prx.password)
211
- else
212
- OCI::ApiClientProxySettings.new(prx.host, prx.port)
213
- end
214
- end
215
-
216
- #############
217
- # API setup #
218
- #############
219
- def generic_api(klass)
220
- api_prx = api_proxy
221
- if config[:use_instance_principals]
222
- sign = OCI::Auth::Signers::InstancePrincipalsSecurityTokenSigner.new
223
- params = { signer: sign }
224
- elsif config[:use_token_auth]
225
- pkey_content = oci_config.key_content || IO.read(oci_config.key_file).strip
226
- pkey = OpenSSL::PKey::RSA.new(pkey_content, oci_config.pass_phrase)
227
-
228
- token = IO.read(oci_config.security_token_file).strip
229
- sign = OCI::Auth::Signers::SecurityTokenSigner.new(token, pkey)
230
- params = { config: oci_config, signer: sign }
231
- else
232
- params = { config: oci_config }
233
- end
234
- params[:proxy_settings] = api_prx if api_prx
235
- klass.new(**params)
236
- end
237
-
238
- def comp_api
239
- generic_api(OCI::Core::ComputeClient)
240
- end
241
-
242
- def net_api
243
- generic_api(OCI::Core::VirtualNetworkClient)
244
- end
245
-
246
- def dbaas_api
247
- generic_api(OCI::Database::DatabaseClient)
248
- end
249
-
250
- def ident_api
251
- generic_api(OCI::Identity::IdentityClient)
252
- end
253
-
254
- def blockstorage_api
255
- generic_api(OCI::Core::BlockstorageClient)
256
- end
257
-
258
- ##################
259
- # Common methods #
260
- ##################
261
- def launch_instance(state)
262
- if instance_type == 'compute'
263
- launch_compute_instance(state)
264
- elsif instance_type == 'dbaas'
265
- launch_dbaas_instance
266
- end
267
- end
268
-
269
- def public_ip_allowed?
270
- subnet = net_api.get_subnet(config[:subnet_id]).data
271
- !subnet.prohibit_public_ip_on_vnic
272
- end
273
-
274
- def instance_ip(instance_id)
275
- if instance_type == 'compute'
276
- compute_instance_ip(instance_id)
277
- elsif instance_type == 'dbaas'
278
- dbaas_instance_ip(instance_id)
279
- end
280
- end
281
-
282
- def pubkey
283
- if instance_type == 'compute'
284
- File.readlines(config[:ssh_keypath]).first.chomp
285
- elsif instance_type == 'dbaas'
286
- result = []
287
- result << File.readlines(config[:ssh_keypath]).first.chomp
288
- end
289
- end
290
-
291
- def generate_hostname
292
- prefix = config[:hostname_prefix]
293
- if instance_type == 'compute'
294
- [prefix, random_hostname(instance.name)].compact.join('-')
295
- elsif instance_type == 'dbaas'
296
- # 30 character limit for hostname in DBaaS
297
- if prefix.length >= 30
298
- [prefix[0, 26], 'db1'].compact.join('-')
299
- else
300
- [prefix, random_string(25 - prefix.length), 'db1'].compact.join('-')
301
- end
302
- end
303
- end
304
-
305
- def random_hostname(prefix)
306
- "#{prefix}-#{random_string(6)}"
307
- end
308
-
309
- def random_password
310
- if instance_type == 'compute'
311
- special_chars = %w[@ - ( ) .]
312
- elsif instance_type == 'dbaas'
313
- special_chars = %w[# _ -]
314
- end
315
-
316
- (Array.new(5) { special_chars.sample } +
317
- Array.new(5) { ('a'..'z').to_a.sample } +
318
- Array.new(5) { ('A'..'Z').to_a.sample } +
319
- Array.new(5) { ('0'..'9').to_a.sample }).shuffle.join
320
- end
321
-
322
- def random_string(length)
323
- Array.new(length) { ('a'..'z').to_a.sample }.join
324
- end
325
-
326
- def random_number(length)
327
- Array.new(length) { ('0'..'9').to_a.sample }.join
328
- end
329
-
330
- ###################
331
- # Compute methods #
332
- ###################
333
- def launch_compute_instance(state)
334
- request = compute_instance_request(state)
335
- response = comp_api.launch_instance(request)
336
- instance_id = response.data.id
337
-
338
- comp_api.get_instance(instance_id).wait_until(
339
- :lifecycle_state,
340
- OCI::Core::Models::Instance::LIFECYCLE_STATE_RUNNING
341
- )
342
- instance_id
343
- end
344
-
345
- def compute_instance_request(state)
346
- request = compute_launch_details
347
-
348
- inject_powershell(state) if config[:setup_winrm] == true
349
-
350
- metadata = {}
351
- md = config[:custom_metadata]
352
- md.each do |key, value|
353
- metadata.store(key, value)
354
- end
355
- metadata.store('ssh_authorized_keys', pubkey)
356
- data = user_data
357
- metadata.store('user_data', data) if config[:user_data] && !config[:user_data].empty?
358
- request.metadata = metadata
359
- request
360
- end
361
-
362
- def compute_launch_details # rubocop:disable Metrics/MethodLength
363
- OCI::Core::Models::LaunchInstanceDetails.new.tap do |l|
364
- hostname = generate_hostname
365
- l.availability_domain = config[:availability_domain]
366
- l.compartment_id = compartment_id
367
- l.display_name = hostname
368
- l.source_details = instance_source_details
369
- l.shape = config[:shape]
370
- l.create_vnic_details = create_vnic_details(hostname)
371
- l.freeform_tags = process_freeform_tags(config[:freeform_tags])
372
- l.defined_tags = config[:defined_tags]
373
- l.preemptible_instance_config = preemptible_instance_config if config[:preemptible_instance]
374
- l.shape_config = shape_config unless config[:shape_config].empty?
375
- end
376
- end
377
-
378
- def instance_source_details
379
- OCI::Core::Models::InstanceSourceViaImageDetails.new(
380
- sourceType: 'image',
381
- imageId: config[:image_id],
382
- bootVolumeSizeInGBs: config[:boot_volume_size_in_gbs],
383
- )
384
- end
385
-
386
- def preemptible_instance_config
387
- OCI::Core::Models::PreemptibleInstanceConfigDetails.new(
388
- preemption_action:
389
- OCI::Core::Models::TerminatePreemptionAction.new(
390
- type: 'TERMINATE', preserve_boot_volume: true
391
- )
392
- )
393
- end
394
-
395
- def shape_config
396
- OCI::Core::Models::LaunchInstanceShapeConfigDetails.new(
397
- ocpus: config[:shape_config][:ocpus],
398
- memory_in_gbs: config[:shape_config][:memory_in_gbs],
399
- baseline_ocpu_utilization: config[:shape_config][:baseline_ocpu_utilization] || 'BASELINE_1_1'
400
- )
401
- end
402
-
403
- def create_vnic_details(name)
404
- nsg_ids = config[:nsg_ids] || []
405
- raise 'nsg_ids cannot have more than 5 NSGs.' if nsg_ids.length > 5
406
- OCI::Core::Models::CreateVnicDetails.new(
407
- assign_public_ip: public_ip_allowed?,
408
- display_name: name,
409
- hostname_label: name,
410
- nsg_ids: nsg_ids,
411
- subnetId: config[:subnet_id]
412
- )
413
- end
414
-
415
- def vnics(instance_id)
416
- vnic_attachments(instance_id).map do |att|
417
- net_api.get_vnic(att.vnic_id).data
418
- end
419
- end
420
-
421
- def vnic_attachments(instance_id)
422
- att = comp_api.list_vnic_attachments(
423
- compartment_id,
424
- instance_id: instance_id
425
- ).data
426
-
427
- raise 'Could not find any VNIC attachments' unless att.any?
428
-
429
- att
430
- end
431
-
432
- def compute_instance_ip(instance_id)
433
- vnic = vnics(instance_id).select(&:is_primary).first
434
- if public_ip_allowed?
435
- config[:use_private_ip] ? vnic.private_ip : vnic.public_ip
436
- else
437
- vnic.private_ip
438
- end
439
- end
440
-
441
- def winrm_ps1(state)
442
- filename = File.join(__dir__, %w[.. .. .. tpl setup_winrm.ps1.erb])
443
- tpl = ERB.new(File.read(filename))
444
- tpl.result(binding)
445
- end
446
-
447
- def inject_powershell(state)
448
- data = winrm_ps1(state)
449
- config[:user_data] ||= []
450
- config[:user_data] << {
451
- type: 'x-shellscript',
452
- inline: data,
453
- filename: 'setup_winrm.ps1'
454
- }
455
- end
456
-
457
- def read_part(part)
458
- if part[:path]
459
- content = File.read part[:path]
460
- elsif part[:inline]
461
- content = part[:inline]
462
- else
463
- raise 'Invalid user data'
464
- end
465
- content.split("\n")
466
- end
79
+ # dbaas configs
80
+ default_config :dbaas, {}
467
81
 
468
- def mime_parts(boundary)
469
- msg = []
470
- config[:user_data].each do |m|
471
- msg << "--#{boundary}"
472
- msg << "Content-Disposition: attachment; filename=\"#{m[:filename]}\""
473
- msg << 'Content-Transfer-Encoding: 7bit'
474
- msg << "Content-Type: text/#{m[:type]}" << 'Mime-Version: 1.0' << ''
475
- msg << read_part(m) << ''
476
- end
477
- msg << "--#{boundary}--"
478
- msg
82
+ validations[:instance_type] = lambda do |attr, val, driver|
83
+ validation_error("[:#{attr}] #{val} is not a valid instance_type. must be either compute or dbaas.", driver) unless %w{compute dbaas}.include?(val.downcase)
479
84
  end
480
85
 
481
- def user_data # rubocop:disable Metrics/MethodLength
482
- if config[:user_data].is_a? Array
483
- boundary = "MIMEBOUNDARY_#{random_string(20)}"
484
- msg = ["Content-Type: multipart/mixed; boundary=\"#{boundary}\"",
485
- 'MIME-Version: 1.0', '']
486
- msg += mime_parts(boundary)
487
- txt = msg.join("\n") + "\n"
488
- gzip = Zlib::GzipWriter.new(StringIO.new)
489
- gzip << txt
490
- Base64.encode64(gzip.close.string).delete("\n")
491
- elsif config[:user_data].is_a? String
492
- Base64.encode64(config[:user_data]).delete("\n")
493
- end
86
+ validations[:nsg_ids] = lambda do |attr, val, driver|
87
+ validation_error("[:#{attr}] list cannot be longer than 5 items", driver) if val.length > 5
494
88
  end
495
89
 
496
- ########################
497
- # BlockStorage methods #
498
- ########################
499
- def process_volumes_list(state)
500
- created_vol = []
501
- config[:volumes].each do |vol_settings|
502
- # convert to hash because otherwise it's an an OCI API Object and won't load
503
- volume_attachment_type = vol_settings[:type] ? vol_settings[:type].downcase : 'paravirtual'
504
- unless %w[iscsi paravirtual].include?(volume_attachment_type)
505
- info("invalid volume attachment type: #{volume_attachment_type}")
506
- next
90
+ validations[:volumes] = lambda do |attr, val, driver|
91
+ val.each do |vol_attr|
92
+ unless ["iscsi", "paravirtual", nil].include?(vol_attr[:type])
93
+ validation_error("[:#{attr}][:type] #{vol_attr[:type]} is not a valid volume type for #{vol_attr[:name]}", driver)
507
94
  end
508
- volume = volume_create(
509
- config[:availability_domain],
510
- vol_settings[:name],
511
- vol_settings[:size_in_gbs],
512
- vol_settings[:vpus_per_gb] || 10
513
- ).to_hash
514
- # convert to string otherwise it's a ruby datetime object and won't load
515
- volume[:attachment_type] = volume_attachment_type
516
- created_vol << volume
517
- end
518
- created_vol
519
- end
520
-
521
- def volume_create(availability_domain, display_name, size_in_gbs, vpus_per_gb)
522
- info("Creating <#{display_name}>...")
523
- result = blockstorage_api.create_volume(
524
- OCI::Core::Models::CreateVolumeDetails.new(
525
- compartment_id: compartment_id,
526
- availability_domain: availability_domain,
527
- display_name: display_name,
528
- size_in_gbs: size_in_gbs,
529
- vpus_per_gb: vpus_per_gb
530
- )
531
- )
532
- get_volume_response = blockstorage_api.get_volume(result.data.id)
533
- .wait_until(:lifecycle_state, OCI::Core::Models::Volume::LIFECYCLE_STATE_AVAILABLE)
534
- info("Finished creating <#{display_name}>.")
535
- {
536
- id: get_volume_response.data.id,
537
- display_name: get_volume_response.data.display_name
538
- }
539
- end
540
-
541
- def volume_delete(volume_id)
542
- info("Deleting <#{volume_id}>...")
543
- blockstorage_api.delete_volume(volume_id)
544
- blockstorage_api.get_volume(volume_id)
545
- .wait_until(:lifecycle_state, OCI::Core::Models::Volume::LIFECYCLE_STATE_TERMINATED)
546
- info("Finished deleting <#{volume_id}>.")
547
- end
548
-
549
- def process_volume_attachments(state)
550
- attachments = []
551
- state[:volumes].each do |volume|
552
- info("Attaching <#{volume[:display_name]}>...")
553
- details = volume_create_attachment_details(volume, state[:server_id])
554
- attachment = volume_attach(details).to_hash
555
- attachments << attachment
556
- info("Finished attaching <#{volume[:display_name]}>.")
557
- end
558
- attachments
559
- end
560
-
561
- def volume_create_attachment_details(volume, instance_id)
562
- if volume[:attachment_type].eql?('iscsi')
563
- OCI::Core::Models::AttachIScsiVolumeDetails.new(
564
- display_name: 'iSCSIAttachment',
565
- volume_id: volume[:id],
566
- instance_id: instance_id
567
- )
568
- elsif volume[:attachment_type].eql?('paravirtual')
569
- OCI::Core::Models::AttachParavirtualizedVolumeDetails.new(
570
- display_name: 'paravirtAttachment',
571
- volume_id: volume[:id],
572
- instance_id: instance_id
573
- )
574
- end
575
- end
576
-
577
- def volume_attach(volume_attachment_details)
578
- result = comp_api.attach_volume(volume_attachment_details)
579
- get_volume_attachment_response =
580
- comp_api.get_volume_attachment(result.data.id)
581
- .wait_until(:lifecycle_state, OCI::Core::Models::VolumeAttachment::LIFECYCLE_STATE_ATTACHED)
582
- state_data = {
583
- id: get_volume_attachment_response.data.id
584
- }
585
- if get_volume_attachment_response.data.attachment_type == 'iscsi'
586
- state_data.store(:iqn_ipv4, get_volume_attachment_response.data.ipv4)
587
- state_data.store(:iqn, get_volume_attachment_response.data.iqn)
588
- state_data.store(:port, get_volume_attachment_response.data.port)
589
95
  end
590
- state_data
591
96
  end
592
97
 
593
- def volume_detach(volume_attachment)
594
- info("Detaching <#{volume_attachment[:id]}>...")
595
- comp_api.detach_volume(volume_attachment[:id])
596
- comp_api.get_volume_attachment(volume_attachment[:id])
597
- .wait_until(:lifecycle_state, OCI::Core::Models::VolumeAttachment::LIFECYCLE_STATE_DETACHED)
598
- info("Finished detaching <#{volume_attachment[:id]}>.")
98
+ def self.validation_error(message, driver)
99
+ raise UserError, "#{driver.class}<#{driver.instance.name}>#config#{message}"
599
100
  end
600
101
 
601
- #################
602
- # DBaaS methods #
603
- #################
604
- def launch_dbaas_instance
605
- request = dbaas_launch_details
606
- response = dbaas_api.launch_db_system(request)
607
- instance_id = response.data.id
608
-
609
- dbaas_api.get_db_system(instance_id).wait_until(
610
- :lifecycle_state,
611
- OCI::Database::Models::DbSystem::LIFECYCLE_STATE_AVAILABLE,
612
- max_interval_seconds: 900,
613
- max_wait_seconds: 21600
614
- )
615
- instance_id
616
- end
102
+ include Kitchen::Driver::Oci::Models
617
103
 
618
- def dbaas_launch_details # rubocop:disable Metrics/MethodLength
619
- cpu_core_count = config[:dbaas][:cpu_core_count] ||= 2
620
- database_edition = config[:dbaas][:database_edition] ||= OCI::Database::Models::DbSystem::DATABASE_EDITION_ENTERPRISE_EDITION
621
- initial_data_storage_size_in_gb = config[:dbaas][:initial_data_storage_size_in_gb] ||= 256
622
- license_model = config[:dbaas][:license_model] ||= OCI::Database::Models::DbSystem::LICENSE_MODEL_BRING_YOUR_OWN_LICENSE
104
+ def create(state)
105
+ return if state[:server_id]
623
106
 
624
- OCI::Database::Models::LaunchDbSystemDetails.new.tap do |l|
625
- l.availability_domain = config[:availability_domain]
626
- l.compartment_id = compartment_id
627
- l.cpu_core_count = cpu_core_count
628
- l.database_edition = database_edition
629
- l.db_home = create_db_home_details
630
- l.display_name = [config[:hostname_prefix], random_string(4), random_number(2)].compact.join('-')
631
- l.hostname = generate_hostname
632
- l.shape = config[:shape]
633
- l.ssh_public_keys = pubkey
634
- l.cluster_name = generate_cluster_name
635
- l.initial_data_storage_size_in_gb = initial_data_storage_size_in_gb
636
- l.node_count = 1
637
- l.license_model = license_model
638
- l.subnet_id = config[:subnet_id]
639
- l.freeform_tags = process_freeform_tags(config[:freeform_tags])
640
- l.defined_tags = config[:defined_tags]
641
- end
107
+ validate_config!
108
+ oci, api = auth(__method__)
109
+ inst = instance_class(config, state, oci, api, __method__)
110
+ state_details = inst.launch
111
+ state.merge!(state_details)
112
+ instance.transport.connection(state).wait_until_ready
113
+ create_and_attach_volumes(config, state, oci, api)
114
+ process_post_script
642
115
  end
643
116
 
644
- def create_db_home_details
645
- raise 'db_version cannot be nil!' if config[:dbaas][:db_version].nil?
117
+ def destroy(state)
118
+ return unless state[:server_id]
646
119
 
647
- OCI::Database::Models::CreateDbHomeDetails.new.tap do |l|
648
- l.database = create_database_details
649
- l.db_version = config[:dbaas][:db_version]
650
- l.display_name = ['dbhome', random_number(10)].compact.join('')
651
- end
120
+ oci, api = auth(__method__)
121
+ instance.transport.connection(state).close
122
+ detatch_and_delete_volumes(state, oci, api) if state[:volumes]
123
+ inst = instance_class(config, state, oci, api, __method__)
124
+ inst.terminate
652
125
  end
653
126
 
654
- def create_database_details # rubocop:disable Metrics/MethodLength
655
- character_set = config[:dbaas][:character_set] ||= 'AL32UTF8'
656
- ncharacter_set = config[:dbaas][:ncharacter_set] ||= 'AL16UTF16'
657
- db_workload = config[:dbaas][:db_workload] ||= OCI::Database::Models::CreateDatabaseDetails::DB_WORKLOAD_OLTP
658
- admin_password = config[:dbaas][:admin_password] ||= random_password
659
- db_name = config[:dbaas][:db_name] ||= 'dbaas1'
127
+ private
660
128
 
661
- OCI::Database::Models::CreateDatabaseDetails.new.tap do |l|
662
- l.admin_password = admin_password
663
- l.character_set = character_set
664
- l.db_name = db_name
665
- l.db_workload = db_workload
666
- l.ncharacter_set = ncharacter_set
667
- l.pdb_name = config[:dbaas][:pdb_name]
668
- l.db_backup_config = db_backup_config
669
- end
129
+ def auth(action)
130
+ oci = Oci::Config.new(config)
131
+ api = Oci::Api.new(oci.config, config)
132
+ oci.compartment if action == :create
133
+ [oci, api]
670
134
  end
671
135
 
672
- def db_backup_config
673
- OCI::Database::Models::DbBackupConfig.new.tap do |l|
674
- l.auto_backup_enabled = false
675
- end
676
- end
136
+ def create_and_attach_volumes(config, state, oci, api)
137
+ return if config[:volumes].empty?
677
138
 
678
- def generate_cluster_name
679
- prefix = config[:hostname_prefix].split('-')[0]
680
- # 11 character limit for cluster_name in DBaaS
681
- if prefix.length >= 11
682
- prefix[0, 11]
683
- else
684
- [prefix, random_string(10 - prefix.length)].compact.join('-')
139
+ volume_state = { volumes: [], volume_attachments: [] }
140
+ config[:volumes].each do |volume|
141
+ vol = volume_class(volume[:type], config, state, oci, api)
142
+ volume_details, vol_state = vol.create_volume(volume)
143
+ attach_state = vol.attach_volume(volume_details, state[:server_id])
144
+ volume_state[:volumes] << vol_state
145
+ volume_state[:volume_attachments] << attach_state
685
146
  end
147
+ state.merge!(volume_state)
686
148
  end
687
149
 
688
- def dbaas_node(instance_id)
689
- dbaas_api.list_db_nodes(
690
- compartment_id,
691
- db_system_id: instance_id
692
- ).data
150
+ def detatch_and_delete_volumes(state, oci, api)
151
+ bls = Blockstorage.new(config, state, oci, api, :destroy)
152
+ state[:volume_attachments].each { |att| bls.detatch_volume(att) }
153
+ state[:volumes].each { |vol| bls.delete_volume(vol) }
693
154
  end
694
155
 
695
- def dbaas_vnic(node_ocid)
696
- dbaas_api.get_db_node(node_ocid).data
697
- end
156
+ def process_post_script
157
+ return if config[:post_create_script].nil?
698
158
 
699
- def dbaas_instance_ip(instance_id)
700
- vnic = dbaas_node(instance_id).select(&:vnic_id).first.vnic_id
701
- if public_ip_allowed?
702
- net_api.get_vnic(vnic).data.public_ip
703
- else
704
- net_api.get_vnic(vnic).data.private_ip
705
- end
159
+ info("Running post create script")
160
+ script = config[:post_create_script]
161
+ instance.transport.connection(state).execute(script)
706
162
  end
707
163
  end
708
164
  end
709
165
  end
710
-
711
- # rubocop:enable Metrics/AbcSize