kitchen-oci 1.12.1 → 1.13.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca0eb69dede4d52baea704de587e6482148229f09a26137993a7a3fc2cf05051
4
- data.tar.gz: 7555a7a550d8db12668014d1bb9a587b707144e688fe3de3be74e796247d417a
3
+ metadata.gz: 7e1357de8069c06276801117608aa999544eee7db3af9f4829b65236600ef602
4
+ data.tar.gz: fe073de0b5ab64c48dbc706df628ce7e1fed54f865234732fc484778b68c6106
5
5
  SHA512:
6
- metadata.gz: d2e53b809addf74284e222b0c402a177ce4a777dd6df1ba7c1e621c5fc9b3cd18c56d2833ef159615aa715a1dd14347fe31a62c2426c477b0e820c21ae1c5a91
7
- data.tar.gz: b6c543c4342706e53a0d86de012ed61a8e55f4c063bf9c039cd4585e7ad87e71f0d75d2cee8b3bdfb0b59210a75fc93de011e5680a6b24c25872a13a4663c04f
6
+ metadata.gz: 454e20b024cf7c4c1b2615db89b51b7897561484aa633a01c3b5a2332fcb989903cf556a3de1a589b5390ab8021940dc6840b7f8bb0955b5bc613713382b9df3
7
+ data.tar.gz: 141394aa371177bdfff43273d91e276879afc16159c1faa01bcdce5f4db527373a4d14abd70033d0d2f427ed8c46a44cbaa67557da65d654f396d412a8e4f70d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # kitchen-oci CHANGELOG
2
+
3
+ # 1.13.0
4
+ - feat: add Network Security Group support
5
+ - feat: add support for attaching multiple volumes
6
+
7
+ ## 1.12.3
8
+ - feat: add support for specifying compartment by name
1
9
 
2
10
  ## 1.12.1
3
11
  - Refactor `oci_config` method to account for `warning: Using the last argument as keyword parameters is deprecated` deprecation warning
data/README.md CHANGED
@@ -53,7 +53,7 @@ gem install pkg/kitchen-oci-<VERSION>.gem
53
53
 
54
54
  Adjust below template as required. The following configuration is mandatory for all instance types:
55
55
 
56
- - `compartment_id`
56
+ - `compartment_id` or `compartment_name`
57
57
  - `availability_domain`
58
58
  - `shape`
59
59
  - `subnet_id`
@@ -74,9 +74,11 @@ The following configuration is mandatory:
74
74
 
75
75
  These settings are optional:
76
76
 
77
+ - `boot_volume_size_in_gbs`, The size of the boot volume, in GB
77
78
  - `use_private_ip`, Whether to connect to the instance using a private IP, default is false (public ip)
78
79
  - `oci_config_file`, OCI configuration file, by default this is ~/.oci/config
79
80
  - `oci_profile_name`, OCI profile to use, default value is "DEFAULT"
81
+ - `oci_config`, Hash of additional `OCI::Config` settings. Allows you to test without an oci config file (see below)
80
82
  - `ssh_keypath`, SSH public key, default is ~/.ssh/id\_rsa.pub
81
83
  - `post_create_script`, run a script on compute\_instance after deployment
82
84
  - `proxy_url`, Connect via the specified proxy URL
@@ -84,11 +86,18 @@ These settings are optional:
84
86
  - `hostname_prefix`, Prefix for the generated hostnames (note that OCI doesn't like underscores)
85
87
  - `freeform_tags`, Hash containing tag name(s) and values(s)
86
88
  - `use_instance_principals`, Boolean flag indicated whether Instance Principals should be used as credentials (see below)
89
+ - `use_token_auth`, Boolean flag indicating if token authentication should be used (see below)
87
90
  - `preemptible_instance`, Boolean flag to indicate if the compute instance should be preemptible, default is `false`.
88
91
  - `shape_config`, Hash of shape config parameters required when using Flex shapes.
89
92
  - `ocpus`, number of CPUs requested
90
93
  - `memory_in_gbs`, the amount of memory requested
91
94
  - `baseline_ocpu_utilization`, the minimum CPU utilization, default `BASELINE_1_1`
95
+ - `volumes`, an array of hashes with configuration options of each volume
96
+ - `name`, the display name of the volume
97
+ - `size_in_gbs`, the size in Gbs for the volume. Can't be lower than 50Gbs (Oracle Limit)
98
+ - `type`, oracle only supports `iscsi` or `paravirtual` options (default: `paravirtual`)
99
+ - `vpus_per_gb`, vpus per gb. Make sure to consult the documentation for your shape to take advantage of UHP as MultiPath is enabled only with certain combinations of memory/cpus.
100
+ - `nsg_ids`, The option to connect up to 5 Network Security Groups to compute instance.
92
101
 
93
102
  Optional settings for WinRM support in Windows:
94
103
 
@@ -105,7 +114,7 @@ If the `subnet_id` refers to a subnet configured to disallow public IPs on any a
105
114
  driver:
106
115
  name: oci
107
116
  # These are mandatory
108
- compartment_id: "ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
117
+ compartment_name: "dev-00"
109
118
  availability_domain: "XyAb:US-ASHBURN-AD-1"
110
119
  image_id: "ocid1.image.oc1.phx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
111
120
  shape: "VM.Standard1.2"
@@ -116,6 +125,9 @@ If the `subnet_id` refers to a subnet configured to disallow public IPs on any a
116
125
  oci_config_file: "~/.oci/config"
117
126
  oci_profile_name: "DEFAULT"
118
127
  ssh_keypath: "~/.ssh/id_rsa.pub"
128
+ nsg_ids:
129
+ - ocid1.networksecuritygroup.oc1.phx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
130
+ - ocid1.networksecuritygroup.oc1.phx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
119
131
  post_create_script: >-
120
132
  ```
121
133
 
@@ -173,6 +185,43 @@ export no_proxy=169.254.0.0/16
173
185
 
174
186
  This will allow the OCI lib to retrieve the certificate, key and ca-chain from the metadata service.
175
187
 
188
+ ## Token Auth
189
+
190
+ If you are launching Kitchen from system configured for token authentication (by running `oci session authenticate`), you need to set `use_token_auth: true`. This is in addition to the `oci_config_file` and `oci_profile_name` settings.
191
+
192
+ ```yml
193
+ platforms:
194
+ - name: ubuntu-18.04
195
+ driver:
196
+ ...
197
+ oci_config_file: "~/.oci/config"
198
+ oci_profile_name: "DEFAULT"
199
+ use_token_auth: true
200
+ ...
201
+ ```
202
+
203
+ ## Use without OCI config file
204
+
205
+ If you want to run without running `oci setup config` (such as on a build server) you can specify configuration settings that would be in the `~/.oci/config` file directly in the `kitchen.yml`
206
+
207
+ For example, to use the [OCI CLI Environment Variables](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/clienvironmentvariables.htm) without a config you could have use kitchen's ERB to read environment variables.
208
+
209
+ ```yml
210
+ platforms:
211
+ - name: ubuntu-18.04
212
+ driver:
213
+ ...
214
+ oci_config:
215
+ region: <%= ENV['OCI_CLI_REGION'] %>
216
+ user: <%= ENV['OCI_CLI_USER'] %>
217
+ fingerprint: <%= ENV['OCI_CLI_FINGERPRINT'] %>
218
+ authentication_type: <%= ENV['OCI_CLI_AUTH'] %>
219
+ key_file: <%= ENV['OCI_CLI_KEY_FILE'] %>
220
+ tenancy: <%= ENV['OCI_CLI_TENANCY'] %>
221
+ ...
222
+ ```
223
+
224
+
176
225
  ## Support for user data scripts and cloud-init
177
226
 
178
227
  The driver has support for adding user data that can be executed as scripts by cloud-init. These can either be specified inline or by referencing a file. Examples:
data/kitchen-oci.gemspec CHANGED
@@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
35
35
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
36
36
  spec.require_paths = ['lib']
37
37
 
38
- spec.add_dependency 'oci', '~> 2.15.0'
38
+ spec.add_dependency 'oci', '~> 2.18.0'
39
39
  spec.add_dependency 'test-kitchen'
40
40
 
41
41
  spec.add_development_dependency 'bundler'
@@ -25,6 +25,7 @@ require 'base64'
25
25
  require 'erb'
26
26
  require 'kitchen'
27
27
  require 'oci'
28
+ require 'openssl'
28
29
  require 'uri'
29
30
  require 'zlib'
30
31
 
@@ -35,12 +36,13 @@ module Kitchen
35
36
  # @author Stephen Pearson <stephen.pearson@oracle.com>
36
37
  class Oci < Kitchen::Driver::Base # rubocop:disable Metrics/ClassLength
37
38
  # required config items
38
- required_config :compartment_id
39
39
  required_config :availability_domain
40
40
  required_config :shape
41
41
  required_config :subnet_id
42
42
 
43
43
  # common config items
44
+ default_config :compartment_id, nil
45
+ default_config :compartment_name, nil
44
46
  default_config :instance_type, 'compute'
45
47
  default_config :hostname_prefix, nil
46
48
  default_keypath = File.expand_path(File.join(%w[~ .ssh id_rsa.pub]))
@@ -52,19 +54,25 @@ module Kitchen
52
54
 
53
55
  # compute config items
54
56
  default_config :image_id
57
+ default_config :boot_volume_size_in_gbs, nil
55
58
  default_config :use_private_ip, false
59
+ default_config :oci_config, {}
56
60
  default_config :oci_config_file, nil
57
61
  default_config :oci_profile_name, nil
58
62
  default_config :setup_winrm, false
59
63
  default_config :winrm_user, 'opc'
60
64
  default_config :winrm_password, nil
61
65
  default_config :use_instance_principals, false
66
+ default_config :use_token_auth, false
62
67
  default_config :preemptible_instance, false
63
68
  default_config :shape_config, {}
64
69
 
65
70
  # dbaas config items
66
71
  default_config :dbaas, {}
67
72
 
73
+ # blockstorage config items
74
+ default_config :volumes, {}
75
+
68
76
  def create(state)
69
77
  return if state[:server_id]
70
78
 
@@ -77,6 +85,9 @@ module Kitchen
77
85
 
78
86
  instance.transport.connection(state).wait_until_ready
79
87
 
88
+ state[:volumes] = process_volumes_list(state)
89
+ state[:volume_attachments] = process_volume_attachments(state)
90
+
80
91
  return unless config[:post_create_script]
81
92
 
82
93
  info('Running post create script')
@@ -89,6 +100,18 @@ module Kitchen
89
100
 
90
101
  instance.transport.connection(state).close
91
102
 
103
+ if state[:volume_attachments]
104
+ state[:volume_attachments].each do |attachment|
105
+ volume_detach(attachment)
106
+ end
107
+ end
108
+
109
+ if state[:volumes]
110
+ state[:volumes].each do |vol|
111
+ volume_delete(vol[:id])
112
+ end
113
+ end
114
+
92
115
  if instance_type == 'compute'
93
116
  comp_api.terminate_instance(state[:server_id])
94
117
  elsif instance_type == 'dbaas'
@@ -121,6 +144,24 @@ module Kitchen
121
144
 
122
145
  private
123
146
 
147
+ def compartment_id
148
+ return config[:compartment_id] if config[:compartment_id]
149
+ raise 'must specify either compartment_id or compartment_name' unless config[:compartment_name]
150
+ ident_api.list_compartments(tenancy).data.find do |item|
151
+ return item.id if item.name == config[:compartment_name]
152
+ end
153
+ raise 'compartment not found'
154
+ end
155
+
156
+ def tenancy
157
+ if config[:use_instance_principals]
158
+ sign = OCI::Auth::Signers::InstancePrincipalsSecurityTokenSigner.new
159
+ sign.instance_variable_get '@tenancy_id'
160
+ else
161
+ oci_config.tenancy
162
+ end
163
+ end
164
+
124
165
  def instance_type
125
166
  raise 'instance_type must be either compute or dbaas!' unless %w[compute dbaas].include?(config[:instance_type].downcase)
126
167
 
@@ -131,10 +172,23 @@ module Kitchen
131
172
  # OCI config setup #
132
173
  ####################
133
174
  def oci_config
175
+ # OCI::Config is missing this
176
+ OCI::Config.class_eval { attr_accessor :security_token_file } if config[:use_token_auth]
177
+
134
178
  opts = {}
135
179
  opts[:config_file_location] = config[:oci_config_file] if config[:oci_config_file]
136
180
  opts[:profile_name] = config[:oci_profile_name] if config[:oci_profile_name]
137
- OCI::ConfigFileLoader.load_config(**opts)
181
+
182
+ oci_config = begin
183
+ OCI::ConfigFileLoader.load_config(**opts)
184
+ rescue OCI::ConfigFileLoader::Errors::ConfigFileNotFoundError
185
+ OCI::Config.new
186
+ end
187
+
188
+ config[:oci_config].each do |key, value|
189
+ oci_config.send("#{key}=", value) unless value.nil? || value.empty?
190
+ end
191
+ oci_config
138
192
  end
139
193
 
140
194
  def proxy_config
@@ -165,6 +219,13 @@ module Kitchen
165
219
  if config[:use_instance_principals]
166
220
  sign = OCI::Auth::Signers::InstancePrincipalsSecurityTokenSigner.new
167
221
  params = { signer: sign }
222
+ elsif config[:use_token_auth]
223
+ pkey_content = oci_config.key_content || IO.read(oci_config.key_file).strip
224
+ pkey = OpenSSL::PKey::RSA.new(pkey_content, oci_config.pass_phrase)
225
+
226
+ token = IO.read(oci_config.security_token_file).strip
227
+ sign = OCI::Auth::Signers::SecurityTokenSigner.new(token, pkey)
228
+ params = { config: oci_config, signer: sign }
168
229
  else
169
230
  params = { config: oci_config }
170
231
  end
@@ -184,6 +245,14 @@ module Kitchen
184
245
  generic_api(OCI::Database::DatabaseClient)
185
246
  end
186
247
 
248
+ def ident_api
249
+ generic_api(OCI::Identity::IdentityClient)
250
+ end
251
+
252
+ def blockstorage_api
253
+ generic_api(OCI::Core::BlockstorageClient)
254
+ end
255
+
187
256
  ##################
188
257
  # Common methods #
189
258
  ##################
@@ -288,7 +357,7 @@ module Kitchen
288
357
  OCI::Core::Models::LaunchInstanceDetails.new.tap do |l|
289
358
  hostname = generate_hostname
290
359
  l.availability_domain = config[:availability_domain]
291
- l.compartment_id = config[:compartment_id]
360
+ l.compartment_id = compartment_id
292
361
  l.display_name = hostname
293
362
  l.source_details = instance_source_details
294
363
  l.shape = config[:shape]
@@ -302,7 +371,8 @@ module Kitchen
302
371
  def instance_source_details
303
372
  OCI::Core::Models::InstanceSourceViaImageDetails.new(
304
373
  sourceType: 'image',
305
- imageId: config[:image_id]
374
+ imageId: config[:image_id],
375
+ bootVolumeSizeInGBs: config[:boot_volume_size_in_gbs],
306
376
  )
307
377
  end
308
378
 
@@ -324,10 +394,13 @@ module Kitchen
324
394
  end
325
395
 
326
396
  def create_vnic_details(name)
397
+ nsg_ids = config[:nsg_ids] || []
398
+ raise 'nsg_ids cannot have more than 5 NSGs.' if nsg_ids.length > 5
327
399
  OCI::Core::Models::CreateVnicDetails.new(
328
400
  assign_public_ip: public_ip_allowed?,
329
401
  display_name: name,
330
402
  hostname_label: name,
403
+ nsg_ids: nsg_ids,
331
404
  subnetId: config[:subnet_id]
332
405
  )
333
406
  end
@@ -340,7 +413,7 @@ module Kitchen
340
413
 
341
414
  def vnic_attachments(instance_id)
342
415
  att = comp_api.list_vnic_attachments(
343
- config[:compartment_id],
416
+ compartment_id,
344
417
  instance_id: instance_id
345
418
  ).data
346
419
 
@@ -413,6 +486,107 @@ module Kitchen
413
486
  end
414
487
  end
415
488
 
489
+ ########################
490
+ # BlockStorage methods #
491
+ ########################
492
+ def process_volumes_list(state)
493
+ created_vol = []
494
+ config[:volumes].each do |vol_settings|
495
+ # convert to hash because otherwise it's an an OCI API Object and won't load
496
+ volume_attachment_type = vol_settings[:type].downcase || 'paravirtual'
497
+ unless %w[iscsi paravirtual].include?(volume_attachment_type)
498
+ info("invalid volume attachment type: #{volume_attachment_type}")
499
+ next
500
+ end
501
+ volume = volume_create(
502
+ config[:availability_domain],
503
+ vol_settings[:name],
504
+ vol_settings[:size_in_gbs],
505
+ vol_settings[:vpus_per_gb] || 10
506
+ ).to_hash
507
+ # convert to string otherwise it's a ruby datetime object and won't load
508
+ volume[:attachment_type] = volume_attachment_type
509
+ created_vol << volume
510
+ end
511
+ created_vol
512
+ end
513
+
514
+ def volume_create(availability_domain, display_name, size_in_gbs, vpus_per_gb)
515
+ info("Creating volume <#{display_name}>...")
516
+ result = blockstorage_api.create_volume(
517
+ OCI::Core::Models::CreateVolumeDetails.new(
518
+ compartment_id: compartment_id,
519
+ availability_domain: availability_domain,
520
+ display_name: display_name,
521
+ size_in_gbs: size_in_gbs,
522
+ vpus_per_gb: vpus_per_gb
523
+ )
524
+ )
525
+ get_volume_response = blockstorage_api.get_volume(result.data.id)
526
+ .wait_until(:lifecycle_state, OCI::Core::Models::Volume::LIFECYCLE_STATE_AVAILABLE)
527
+ info("Finished creating volume <#{display_name}>.")
528
+ state_data = {
529
+ :id => get_volume_response.data.id
530
+ }
531
+ end
532
+
533
+ def volume_delete(volume_id)
534
+ info("Deleting volume: <#{volume_id}>...")
535
+ blockstorage_api.delete_volume(volume_id)
536
+ blockstorage_api.get_volume(volume_id)
537
+ .wait_until(:lifecycle_state, OCI::Core::Models::Volume::LIFECYCLE_STATE_TERMINATED)
538
+ info("Deleted volume: <#{volume_id}>")
539
+ end
540
+
541
+ def process_volume_attachments(state)
542
+ attachments = []
543
+ state[:volumes].each do |volume|
544
+ info("Attaching Volume: #{volume[:displayName]} - #{volume[:attachment_type]}")
545
+ details = volume_create_attachment_details(volume, state[:server_id])
546
+ attachment = volume_attach(details).to_hash
547
+ attachments << attachment
548
+ info("Attached Volume #{volume[:displayName]} - #{volume[:attachment_type]}")
549
+ end
550
+ attachments
551
+ end
552
+
553
+ def volume_create_attachment_details(volume, instance_id)
554
+ if volume[:attachment_type].eql?('iscsi')
555
+ OCI::Core::Models::AttachIScsiVolumeDetails.new(
556
+ display_name: 'iSCSIAttachment',
557
+ volume_id: volume[:id],
558
+ instance_id: instance_id
559
+ )
560
+ elsif volume[:attachment_type].eq?('paravirtual')
561
+ OCI::Core::Models::AttachParavirtualizedVolumeDetails.new(
562
+ display_name: 'paravirtAttachment',
563
+ volume_id: volume[:id],
564
+ instance_id: instance_id
565
+ )
566
+ end
567
+ end
568
+
569
+ def volume_attach(volume_attachment_details)
570
+ result = comp_api.attach_volume(volume_attachment_details)
571
+ get_volume_attachment_response =
572
+ comp_api.get_volume_attachment(result.data.id)
573
+ .wait_until(:lifecycle_state, OCI::Core::Models::VolumeAttachment::LIFECYCLE_STATE_ATTACHED)
574
+ state_data = {
575
+ :id => get_volume_attachment_response.data.id,
576
+ :iqn_ipv4 => get_volume_attachment_response.data.ipv4,
577
+ :iqn => get_volume_attachment_response.data.iqn,
578
+ :port => get_volume_attachment_response.data.port,
579
+ }
580
+ end
581
+
582
+ def volume_detach(volume_attachment)
583
+ info("Detaching volume: #{volume_attachment[:id]}")
584
+ comp_api.detach_volume(volume_attachment[:id])
585
+ comp_api.get_volume_attachment(volume_attachment[:id])
586
+ .wait_until(:lifecycle_state, OCI::Core::Models::VolumeAttachment::LIFECYCLE_STATE_DETACHED)
587
+ info("Detached volume: #{volume_attachment[:id]}")
588
+ end
589
+
416
590
  #################
417
591
  # DBaaS methods #
418
592
  #################
@@ -438,7 +612,7 @@ module Kitchen
438
612
 
439
613
  OCI::Database::Models::LaunchDbSystemDetails.new.tap do |l|
440
614
  l.availability_domain = config[:availability_domain]
441
- l.compartment_id = config[:compartment_id]
615
+ l.compartment_id = compartment_id
442
616
  l.cpu_core_count = cpu_core_count
443
617
  l.database_edition = database_edition
444
618
  l.db_home = create_db_home_details
@@ -501,7 +675,7 @@ module Kitchen
501
675
 
502
676
  def dbaas_node(instance_id)
503
677
  dbaas_api.list_db_nodes(
504
- config[:compartment_id],
678
+ compartment_id,
505
679
  db_system_id: instance_id
506
680
  ).data
507
681
  end
@@ -20,6 +20,6 @@
20
20
  module Kitchen
21
21
  module Driver
22
22
  # Version string for Oracle OCI Kitchen driver
23
- OCI_VERSION = '1.12.1'
23
+ OCI_VERSION = '1.13.0'
24
24
  end
25
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-oci
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.1
4
+ version: 1.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Pearson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-30 00:00:00.000000000 Z
11
+ date: 2024-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oci
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.15.0
19
+ version: 2.18.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.15.0
26
+ version: 2.18.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: test-kitchen
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -148,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
148
  - !ruby/object:Gem::Version
149
149
  version: '0'
150
150
  requirements: []
151
- rubygems_version: 3.0.3
151
+ rubygems_version: 3.3.7
152
152
  signing_key:
153
153
  specification_version: 4
154
154
  summary: A Test Kitchen Driver for Oracle OCI