kitchen-azurerm 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f2cefad48dc76f5e168f8409b8ea3e002d07312c8684a719084ffdf1b833550
4
- data.tar.gz: 7234c182b57734cd042faaa857a0779aee6bab34aa409f4d9a3b0a7918ef85e5
3
+ metadata.gz: 51b127c1a7e5d1c197dde573ce77d5e9900fe2297d12e5d8cbcd4d6d1e328ccc
4
+ data.tar.gz: d59c1a55f910f415a271a4818a8879a16e06dff3192d3d3a64d5ce1c32313e6c
5
5
  SHA512:
6
- metadata.gz: ea881a871e778781d39573ced02fb2a03ba1bd4c207bddd01ec6e527d85c7efa5d7a1ef378a282216fe14f90831aa284237b2e3bc9bceb6b28f2fb1dc0aa1db5
7
- data.tar.gz: c82d8dabb76c54a73b0e0f444a367001a631bd8f2e28d0108fa17e918827e6a8d83463e1170e72c5b6100acc6ea87fa9fc4d4ab44b98842d810c78cc3bac4fab
6
+ metadata.gz: 1e21e9a0cce3a113f9d6fd9206a9272282eb1d54c637e2c9f5b629a42ccbf144889355d18db7127a7693e088d8056005139cdf142e3232ed70870c72b23db3f0
7
+ data.tar.gz: '0690d8abddf2c745c7631e463e7479299a0e1e8a83af57caca797e8555a840bc0559c383d7d703b35c7bb7ec4c65b7556a15e95cf2b4582c153f8e45d0dd97e9'
data/README.md CHANGED
@@ -670,11 +670,11 @@ info: vm image list command OK
670
670
 
671
671
  * The ```azure_resource_group_prefix``` and ```azure_resource_group_suffix``` can be used to further disambiguate Azure resource group names created by the driver.
672
672
 
673
- * The ```explicit_resource_group_name``` and ```destroy_explicit_resource_group``` (default: "true") parameters can be used in scenarios where you are provided a pre-created Resource Group. Example usage: ```explicit_resource_group_name: kitchen-<%= ENV["USERNAME"] %>```
673
+ * The ```explicit_resource_group_name``` and ```destroy_explicit_resource_group``` (default: "true") parameters can be used in scenarios where you are provided a pre-created Resource Group. Example usage: ```explicit_resource_group_name: kitchen-<%= ENV["USERNAME"] %>```. The ```destroy_explicit_resource_group``` option can now be used after using the ```destroy_resource_group_contents``` option creates an empty resource group to destroy the resource group previously created.
674
674
 
675
- * The ```destroy_resource_group_contents``` (default: "false") parameter can be used when you want to destroy the resources within a resource group without destroying the resource group itself. For example, the following configuration options used in combination would use an existing resource group (or create one if it doesn't exist) and will destroy the contents of the resource group in the ```kitchen destroy``` phase.
675
+ * The ```destroy_resource_group_contents``` (default: "false") parameter can be used when you want to destroy the resources within a resource group without destroying the resource group itself. For example, the following configuration options used in combination would use an existing resource group (or create one if it doesn't exist) and will destroy the contents of the resource group in the ```kitchen destroy``` phase. If you wish to destroy the empty resource group created after you empty the resource group with this flag you can now set the ```destroy_explicit_resource_group``` to "true" to destroy the empty resource group.
676
676
 
677
- * The ```destroy_explicit_resource_group_tags``` (default: "true") parameter can be used when you want to remove tags associated with an explicit resource group. The default setting is set to `true` to remain consistent with previous behavior. This should be used in combination with an explicitly named resource group and will be honored during the ```kitchen destroy``` phase.
677
+ * The ```destroy_explicit_resource_group_tags``` (default: "true") parameter can be used when you want to remove tags associated with an explicit resource group. The default setting is set to "true" to remain consistent with previous behavior. This should be used in combination with an ```explicit_resource_group_name``` and will be honored during the ```kitchen destroy``` phase.
678
678
 
679
679
  ```yaml
680
680
  ---
@@ -688,6 +688,8 @@ driver:
688
688
 
689
689
  * The ```secret_url```, ```vault_name```, and ```vault_resource_group``` parameters can be used to deploy VM with specified key vault certificate.
690
690
 
691
+ * The ```use_fqdn_hostname``` (default: "false") parameter can be used to determine how kitchen communicates with the Virtual Machine. When true, Kitchen will use the FQDN that is assigned to the Virtual Machine. When false, kitchen will use the public IP address of the machine. This may overcome issues with Corporate firewalls or VPNs blocking Public IP addresses.
692
+
691
693
  ## Enabling alternative WinRM configurations
692
694
 
693
695
  * By default on Windows machines, a PowerShell script runs that enables WinRM over the SSL transport, for Basic, Negotiate and CredSSP connections. To supply your own PowerShell script (e.g. to enable HTTP), use the `winrm_powershell_script` parameter. Windows 2008 R2 example:
@@ -1,5 +1,6 @@
1
1
  require "inifile"
2
2
  require "kitchen/logging"
3
+ autoload :MsRest, "ms_rest"
3
4
 
4
5
  module Kitchen
5
6
  module Driver
@@ -60,7 +61,7 @@ module Kitchen
60
61
  if File.file?(config_path)
61
62
  IniFile.load(config_path)
62
63
  else
63
- warn "#{config_path} was not found or not accessible. Will attempt to use Managed Identity."
64
+ warn "#{config_path} was not found or not accessible."
64
65
  {}
65
66
  end
66
67
  end
@@ -71,7 +72,7 @@ module Kitchen
71
72
  end
72
73
 
73
74
  def tenant_id!
74
- tenant_id || raise("Must provide tenant id. Use AZURE_TENANT_ID environment variable or set it in credentials file (#{config_path})")
75
+ tenant_id || warn("(#{config_path}) does not contain tenant_id neither is the AZURE_TENANT_ID environment variable set.")
75
76
  end
76
77
 
77
78
  def tenant_id
@@ -86,13 +87,37 @@ module Kitchen
86
87
  ENV["AZURE_CLIENT_SECRET"] || credentials_property("client_secret")
87
88
  end
88
89
 
90
+ # Retrieve a token based upon the preferred authentication method.
91
+ #
92
+ # @return [::MsRest::TokenProvider] A new token provider object.
89
93
  def token_provider
90
- if client_id && client_secret
94
+ # Login with a credentials file or setting the environment variables
95
+ #
96
+ # Typically used with a service principal.
97
+ #
98
+ # SPN with client_id, client_secret and tenant_id
99
+ if client_id && client_secret && tenant_id
91
100
  ::MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, client_secret, ad_settings)
92
- elsif client_id
101
+ # Login with a Managed Service Identity.
102
+ #
103
+ # Typically used with a Managed Service Identity when you have a particular object registered in a tenant.
104
+ #
105
+ # MSI with client_id and tenant_id (aka User Assigned Identity).
106
+ elsif client_id && tenant_id
93
107
  ::MsRestAzure::MSITokenProvider.new(50342, ad_settings, { client_id: client_id })
94
- else
108
+ # Default approach to inheriting existing object permissions (application or device this code is running on).
109
+ #
110
+ # Typically used when you want to inherit the permissions of the system you're running on that are in a tenant.
111
+ #
112
+ # MSI with just tenant_id (aka System Assigned Identity).
113
+ elsif tenant_id
95
114
  ::MsRestAzure::MSITokenProvider.new(50342, ad_settings)
115
+ # Login using the Azure CLI
116
+ #
117
+ # Typically used when you want to rely upon `az login` as your preferred authentication method.
118
+ else
119
+ warn("Using tenant id set through `az login`.")
120
+ ::MsRestAzure::AzureCliTokenProvider.new(ad_settings)
96
121
  end
97
122
  end
98
123
 
@@ -1,4 +1,6 @@
1
1
  require "kitchen"
2
+
3
+ autoload :MsRestAzure, "ms_rest_azure"
2
4
  require_relative "azure_credentials"
3
5
  require "securerandom" unless defined?(SecureRandom)
4
6
  module Azure
@@ -17,7 +19,9 @@ module Kitchen
17
19
  module Driver
18
20
  #
19
21
  # Azurerm
22
+ # Create a new resource group object and set the location and tags attributes then return it.
20
23
  #
24
+ # @return [::Azure::Resources::Profiles::Latest::Mgmt::Models::ResourceGroup] A new resource group object.
21
25
  class Azurerm < Kitchen::Driver::Base
22
26
  attr_accessor :resource_management_client
23
27
  attr_accessor :network_management_client
@@ -208,6 +212,10 @@ module Kitchen
208
212
  5
209
213
  end
210
214
 
215
+ default_config(:use_fqdn_hostname) do |_config|
216
+ false
217
+ end
218
+
211
219
  def create(state)
212
220
  state = validate_state(state)
213
221
  deployment_parameters = {
@@ -216,7 +224,7 @@ module Kitchen
216
224
  storageAccountType: config[:storage_account_type],
217
225
  bootDiagnosticsEnabled: config[:boot_diagnostics_enabled],
218
226
  newStorageAccountName: "storage#{state[:uuid]}",
219
- adminUsername: state[:username],
227
+ adminUsername: config[:username],
220
228
  dnsNameForPublicIP: "kitchen-#{state[:uuid]}",
221
229
  vmName: state[:vm_name],
222
230
  systemAssignedIdentity: config[:system_assigned_identity],
@@ -227,7 +235,7 @@ module Kitchen
227
235
  }
228
236
 
229
237
  if instance.transport[:ssh_key].nil?
230
- deployment_parameters["adminPassword"] = state[:password]
238
+ deployment_parameters[:adminPassword] = config[:password]
231
239
  end
232
240
 
233
241
  if config[:subscription_id].to_s == ""
@@ -303,6 +311,9 @@ module Kitchen
303
311
  info "Creating deployment: #{deployment_name}"
304
312
  create_deployment_async(state[:azure_resource_group_name], deployment_name, deployment(deployment_parameters)).value!
305
313
  follow_deployment_until_end_state(state[:azure_resource_group_name], deployment_name)
314
+ state[:username] = deployment_parameters[:adminUsername] unless existing_state_value?(state, :username)
315
+ state[:password] = deployment_parameters[:adminPassword] unless existing_state_value?(state, :password) && instance.transport[:ssh_key].nil?
316
+
306
317
  if File.file?(config[:post_deployment_template])
307
318
  post_deployment_name = "post-deploy-#{state[:uuid]}"
308
319
  info "Creating deployment: #{post_deployment_name}"
@@ -328,6 +339,10 @@ module Kitchen
328
339
  result = get_public_ip(state[:azure_resource_group_name], "publicip")
329
340
  info "IP Address is: #{result.ip_address} [#{result.dns_settings.fqdn}]"
330
341
  state[:hostname] = result.ip_address
342
+ if config[:use_fqdn_hostname]
343
+ info "Using FQDN to communicate instead of IP"
344
+ state[:hostname] = result.dns_settings.fqdn
345
+ end
331
346
  else
332
347
  # Retrieve the internal IP from the resource group:
333
348
  result = get_network_interface(state[:azure_resource_group_name], vmnic.to_s)
@@ -336,15 +351,24 @@ module Kitchen
336
351
  end
337
352
  end
338
353
 
354
+ # Return a True of False if the state is already stored for a particular property.
355
+ #
356
+ # @param [Hash] Hash of existing state values.
357
+ # @param [String] A property to check
358
+ # @return [Boolean]
339
359
  def existing_state_value?(state, property)
340
360
  state.key?(property) && !state[property].nil?
341
361
  end
342
362
 
363
+ # Leverage existing state values or bring state into existence from a configuration file.
364
+ #
365
+ # @param [Hash] Existing Hash of state values.
366
+ # @return [Hash] Updated Hash of state values.
343
367
  def validate_state(state = {})
344
368
  state[:uuid] = SecureRandom.hex(8) unless existing_state_value?(state, :uuid)
345
369
  state[:server_id] = "vm#{state[:uuid]}" unless existing_state_value?(state, :server_id)
346
370
  state[:azure_resource_group_name] = azure_resource_group_name unless existing_state_value?(state, :azure_resource_group_name)
347
- %i{subscription_id username password vm_name azure_environment use_managed_disks}.each do |config_element|
371
+ %i{subscription_id vm_name azure_environment use_managed_disks}.each do |config_element|
348
372
  state[config_element] = config[config_element] unless existing_state_value?(state, config_element)
349
373
  end
350
374
  state.delete(:password) unless instance.transport[:ssh_key].nil?
@@ -525,11 +549,38 @@ module Kitchen
525
549
  end
526
550
 
527
551
  def destroy(state)
528
- return if state[:server_id].nil?
552
+ # TODO: We have some not so fun state issues we need to clean up
553
+ state[:azure_environment] = config[:azure_environment] unless state[:azure_environment]
554
+ state[:subscription_id] = config[:subscription_id] unless state[:subscription_id]
529
555
 
556
+ # Setup our authentication components for the SDK
530
557
  options = Kitchen::Driver::AzureCredentials.new(subscription_id: state[:subscription_id],
531
- environment: state[:azure_environment]).azure_options
558
+ environment: state[:azure_environment]).azure_options
532
559
  @resource_management_client = ::Azure::Resources::Profiles::Latest::Mgmt::Client.new(options)
560
+
561
+ # If we don't have any instances, let's check to see if the user wants to delete a resource group and if so let's delete!
562
+ if state[:server_id].nil? && state[:azure_resource_group_name].nil? && !config[:explicit_resource_group_name].nil? && config[:destroy_explicit_resource_group]
563
+ if resource_group_exists?(config[:explicit_resource_group_name])
564
+ info "This instance doesn't exist but you asked to delete the resource group."
565
+ begin
566
+ info "Destroying Resource Group: #{config[:explicit_resource_group_name]}"
567
+ delete_resource_group_async(config[:explicit_resource_group_name])
568
+ info "Destroy operation accepted and will continue in the background."
569
+ return
570
+ rescue ::MsRestAzure::AzureOperationError => operation_error
571
+ error operation_error.body
572
+ raise operation_error
573
+ end
574
+ end
575
+ end
576
+
577
+ # Our working environment
578
+ info "Azure environment: #{state[:azure_environment]}"
579
+
580
+ # Skip if we don't have any instances
581
+ return if state[:server_id].nil?
582
+
583
+ # Destroy resource group contents
533
584
  if config[:destroy_resource_group_contents] == true
534
585
  info "Destroying individual resources within the Resource Group."
535
586
  empty_deployment_name = "empty-deploy-#{state[:uuid]}"
@@ -538,32 +589,20 @@ module Kitchen
538
589
  create_deployment_async(state[:azure_resource_group_name], empty_deployment_name, empty_deployment).value!
539
590
  follow_deployment_until_end_state(state[:azure_resource_group_name], empty_deployment_name)
540
591
 
541
- # Maintain tags on the resource group
542
- if config[:destroy_explicit_resource_group_tags] == false
543
- warn 'The "destroy_explicit_resource_group_tags" setting value is set to "false". The tags on the resource group will NOT be removed.'
544
- # NOTE: We are using the internal wrapper function create_resource_group() which wraps the API
545
- # method of create_or_update().
546
- begin
547
- create_resource_group(state[:azure_resource_group_name], get_resource_group)
548
- rescue ::MsRestAzure::AzureOperationError => operation_error
549
- error operation_error.body
550
- raise operation_error
551
- end
552
- end
553
-
554
- # Corner case where we want to use kitchen to remove the tags
555
- if config[:destroy_explicit_resource_group_tags] == true
556
- warn 'The "destroy_explicit_resource_group_tags" setting value is set to "true". The tags on the resource group will be removed.'
557
- # NOTE: We are using the internal wrapper function create_resource_group() which wraps the API
558
- # method of create_or_update().
592
+ # NOTE: We are using the internal wrapper function create_resource_group() which wraps the API
593
+ # method of create_or_update()
594
+ begin
595
+ # Maintain tags on the resource group
596
+ create_resource_group(state[:azure_resource_group_name], get_resource_group) unless config[:destroy_explicit_resource_group_tags] == true
597
+ warn 'The "destroy_explicit_resource_group_tags" setting value is set to "false". The tags on the resource group will NOT be removed.' unless config[:destroy_explicit_resource_group_tags] == true
598
+ # Corner case where we want to use kitchen to remove the tags
559
599
  resource_group = get_resource_group
560
600
  resource_group.tags = {}
561
- begin
562
- create_resource_group(state[:azure_resource_group_name], resource_group)
563
- rescue ::MsRestAzure::AzureOperationError => operation_error
564
- error operation_error.body
565
- raise operation_error
566
- end
601
+ create_resource_group(state[:azure_resource_group_name], resource_group) unless config[:destroy_explicit_resource_group_tags] == false
602
+ warn 'The "destroy_explicit_resource_group_tags" setting value is set to "true". The tags on the resource group will be removed.' unless config[:destroy_explicit_resource_group_tags] == false
603
+ rescue ::MsRestAzure::AzureOperationError => operation_error
604
+ error operation_error.body
605
+ raise operation_error
567
606
  end
568
607
 
569
608
  rescue ::MsRestAzure::AzureOperationError => operation_error
@@ -571,20 +610,27 @@ module Kitchen
571
610
  raise operation_error
572
611
  end
573
612
  end
613
+
614
+ # Do not remove the explicitly named resource group
574
615
  if config[:destroy_explicit_resource_group] == false && !config[:explicit_resource_group_name].nil?
575
616
  warn 'The "destroy_explicit_resource_group" setting value is set to "false". The resource group will not be deleted.'
576
617
  warn 'Remember to manually destroy resources, or set "destroy_resource_group_contents: true" to save costs!' unless config[:destroy_resource_group_contents] == true
577
- return
618
+ return state
578
619
  end
579
- info "Azure environment: #{state[:azure_environment]}"
620
+
621
+ # Destroy the world
580
622
  begin
581
623
  info "Destroying Resource Group: #{state[:azure_resource_group_name]}"
582
624
  delete_resource_group_async(state[:azure_resource_group_name])
583
625
  info "Destroy operation accepted and will continue in the background."
626
+ # Remove resource group name from driver state
627
+ state.delete(:azure_resource_group_name)
584
628
  rescue ::MsRestAzure::AzureOperationError => operation_error
585
629
  error operation_error.body
586
630
  raise operation_error
587
631
  end
632
+
633
+ # Clear state of components
588
634
  state.delete(:server_id)
589
635
  state.delete(:hostname)
590
636
  state.delete(:username)
@@ -748,6 +794,26 @@ module Kitchen
748
794
  resource_group
749
795
  end
750
796
 
797
+ # Checks whether a resource group exists.
798
+ #
799
+ # @param resource_group_name [String] The name of the resource group to check.
800
+ # The name is case insensitive.
801
+ #
802
+ # @return [Boolean] operation results.
803
+ #
804
+ def resource_group_exists?(resource_group_name)
805
+ retries = config[:azure_api_retries]
806
+ begin
807
+ resource_management_client.resource_groups.check_existence(resource_group_name)
808
+ rescue Faraday::TimeoutError, Faraday::ClientError => exception
809
+ send_exception_message(exception, "while checking if resource group '#{resource_group_name}' exists. #{retries} retries left.")
810
+ raise if retries == 0
811
+
812
+ retries -= 1
813
+ retry
814
+ end
815
+ end
816
+
751
817
  def create_resource_group(resource_group_name, resource_group)
752
818
  retries = config[:azure_api_retries]
753
819
  begin
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-azurerm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stuart Preston
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-09 00:00:00.000000000 Z
11
+ date: 2020-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: azure_mgmt_network
@@ -130,14 +130,14 @@ dependencies:
130
130
  requirements:
131
131
  - - '='
132
132
  - !ruby/object:Gem::Version
133
- version: 1.2.1
133
+ version: 1.4.2
134
134
  type: :development
135
135
  prerelease: false
136
136
  version_requirements: !ruby/object:Gem::Requirement
137
137
  requirements:
138
138
  - - '='
139
139
  - !ruby/object:Gem::Version
140
- version: 1.2.1
140
+ version: 1.4.2
141
141
  - !ruby/object:Gem::Dependency
142
142
  name: rspec
143
143
  requirement: !ruby/object:Gem::Requirement