kitchen-oci 1.15.1 → 1.16.1

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.
@@ -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