From b99419b75d7dbe63f49a0f96bb7be2e6ada0b485 Mon Sep 17 00:00:00 2001 From: Jason Barnett Date: Thu, 16 Apr 2020 18:55:09 -0400 Subject: [PATCH 1/2] Add support for plan information Signed-off-by: Jason Barnett --- README.md | 2 ++ lib/kitchen/driver/azurerm.rb | 20 ++++++++++++++++++-- templates/internal.erb | 3 +++ templates/public.erb | 3 +++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5f8af08..e1f8750 100644 --- a/README.md +++ b/README.md @@ -626,6 +626,8 @@ info: vm image list command OK * The optional ```vm_tags``` parameter allows you to define key:value pairs to tag VMs with on creation. +* The optional ```plan``` parameter allows you to define plan information when creating VMs from Marketplace images. Please refer to [Deploy an image with Marketplace terms](https://aka.ms/azuremarketplaceapideployment) for more details. Not all Marketplace images support programmatic deployment, and support is controlled by the image publisher. + * Managed disks are now enabled by default, to use the Storage account set ```use_managed_disks``` (default: true). * The ```image_url``` (unmanaged disks only) parameter can be used to specify a custom vhd (This VHD must be in the same storage account as the disks of the VM, therefore ```existing_storage_account_blob_url``` must also be set and ```use_managed_disks``` must be set to false) diff --git a/lib/kitchen/driver/azurerm.rb b/lib/kitchen/driver/azurerm.rb index 26193ec..b2fb42c 100644 --- a/lib/kitchen/driver/azurerm.rb +++ b/lib/kitchen/driver/azurerm.rb @@ -132,6 +132,10 @@ class Azurerm < Kitchen::Driver::Base {} end + default_config(:plan) do |_config| + {} + end + default_config(:vm_tags) do |_config| {} end @@ -643,13 +647,25 @@ def windows_unattend_content def virtual_machine_deployment_template if config[:vnet_id] == "" - virtual_machine_deployment_template_file("public.erb", vm_tags: vm_tag_string(config[:vm_tags]), use_managed_disks: config[:use_managed_disks], image_url: config[:image_url], existing_storage_account_blob_url: config[:existing_storage_account_blob_url], image_id: config[:image_id], existing_storage_account_container: config[:existing_storage_account_container], custom_data: config[:custom_data], os_disk_size_gb: config[:os_disk_size_gb], data_disks_for_vm_json: data_disks_for_vm_json, use_ephemeral_osdisk: config[:use_ephemeral_osdisk], ssh_key: instance.transport[:ssh_key]) + virtual_machine_deployment_template_file("public.erb", vm_tags: vm_tag_string(config[:vm_tags]), use_managed_disks: config[:use_managed_disks], image_url: config[:image_url], existing_storage_account_blob_url: config[:existing_storage_account_blob_url], image_id: config[:image_id], existing_storage_account_container: config[:existing_storage_account_container], custom_data: config[:custom_data], os_disk_size_gb: config[:os_disk_size_gb], data_disks_for_vm_json: data_disks_for_vm_json, use_ephemeral_osdisk: config[:use_ephemeral_osdisk], ssh_key: instance.transport[:ssh_key], plan_json: plan_json) else info "Using custom vnet: #{config[:vnet_id]}" - virtual_machine_deployment_template_file("internal.erb", vnet_id: config[:vnet_id], subnet_id: config[:subnet_id], public_ip: config[:public_ip], vm_tags: vm_tag_string(config[:vm_tags]), use_managed_disks: config[:use_managed_disks], image_url: config[:image_url], existing_storage_account_blob_url: config[:existing_storage_account_blob_url], image_id: config[:image_id], existing_storage_account_container: config[:existing_storage_account_container], custom_data: config[:custom_data], os_disk_size_gb: config[:os_disk_size_gb], data_disks_for_vm_json: data_disks_for_vm_json, use_ephemeral_osdisk: config[:use_ephemeral_osdisk], ssh_key: instance.transport[:ssh_key]) + virtual_machine_deployment_template_file("internal.erb", vnet_id: config[:vnet_id], subnet_id: config[:subnet_id], public_ip: config[:public_ip], vm_tags: vm_tag_string(config[:vm_tags]), use_managed_disks: config[:use_managed_disks], image_url: config[:image_url], existing_storage_account_blob_url: config[:existing_storage_account_blob_url], image_id: config[:image_id], existing_storage_account_container: config[:existing_storage_account_container], custom_data: config[:custom_data], os_disk_size_gb: config[:os_disk_size_gb], data_disks_for_vm_json: data_disks_for_vm_json, use_ephemeral_osdisk: config[:use_ephemeral_osdisk], ssh_key: instance.transport[:ssh_key], plan_json: plan_json) end end + def plan_json + return nil if config[:plan].empty? + + plan = {} + plan["name"] = config[:plan][:name] if config[:plan][:name] + plan["product"] = config[:plan][:product] if config[:plan][:product] + plan["promotionCode"] = config[:plan][:promotion_code] if config[:plan][:promotion_code] + plan["publisher"] = config[:plan][:publisher] if config[:plan][:publisher] + + plan.to_json + end + def virtual_machine_deployment_template_file(template_file, data = {}) template = File.read(File.expand_path(File.join(__dir__, "../../../templates", template_file))) render_binding = OpenStruct.new(data) diff --git a/templates/internal.erb b/templates/internal.erb index 3ce21b9..3803cd3 100644 --- a/templates/internal.erb +++ b/templates/internal.erb @@ -410,6 +410,9 @@ <%- end -%> } }, + <%- unless plan_json.nil? -%> + "plan": <%= plan_json %>, + <%- end -%> "identity": { "type": "[variables('vmIdentityType')]", "identityIds": "[if(empty(parameters('userAssignedIdentities')), json('null'), parameters('userAssignedIdentities'))]" diff --git a/templates/public.erb b/templates/public.erb index 383405c..40374fc 100644 --- a/templates/public.erb +++ b/templates/public.erb @@ -429,6 +429,9 @@ <%- end -%> } }, + <%- unless plan_json.nil? -%> + "plan": <%= plan_json %>, + <%- end -%> "identity": { "type": "[variables('vmIdentityType')]", "identityIds": "[if(empty(parameters('userAssignedIdentities')), json('null'), parameters('userAssignedIdentities'))]" From adfb2f47f207d7ed4dae7a2ca4614e406c957e52 Mon Sep 17 00:00:00 2001 From: Jason Barnett Date: Mon, 20 Apr 2020 09:04:20 -0400 Subject: [PATCH 2/2] Build out unit testing Signed-off-by: Jason Barnett --- kitchen-azurerm.gemspec | 4 + lib/kitchen/driver/azurerm.rb | 2 + spec/spec_helper.rb | 1 + spec/unit/kitchen/driver/azurerm_spec.rb | 119 +++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 spec/spec_helper.rb create mode 100644 spec/unit/kitchen/driver/azurerm_spec.rb diff --git a/kitchen-azurerm.gemspec b/kitchen-azurerm.gemspec index 90c532e..d65d532 100644 --- a/kitchen-azurerm.gemspec +++ b/kitchen-azurerm.gemspec @@ -17,8 +17,12 @@ Gem::Specification.new do |spec| spec.add_dependency "azure_mgmt_resources", "~> 0.17", ">= 0.17.2" spec.add_dependency "inifile", "~> 3.0", ">= 3.0.0" spec.add_dependency "sshkey", ">= 1.0.0", "< 3" + spec.add_dependency "test-kitchen", ">= 1.20", "< 3.0" spec.add_development_dependency "bundler" spec.add_development_dependency "rake", ">= 11.0" spec.add_development_dependency "chefstyle" + spec.add_development_dependency "rspec", "~> 3.5" + spec.add_development_dependency "rspec-mocks", "~> 3.5" + spec.add_development_dependency "rspec-expectations", "~> 3.5" end diff --git a/lib/kitchen/driver/azurerm.rb b/lib/kitchen/driver/azurerm.rb index b2fb42c..d2e5aad 100644 --- a/lib/kitchen/driver/azurerm.rb +++ b/lib/kitchen/driver/azurerm.rb @@ -20,6 +20,8 @@ class Azurerm < Kitchen::Driver::Base attr_accessor :resource_management_client attr_accessor :network_management_client + kitchen_driver_api_version 2 + default_config(:azure_resource_group_prefix) do |_config| "kitchen-" end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..b0d915e --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1 @@ +require_relative "../lib/kitchen/driver/azurerm" diff --git a/spec/unit/kitchen/driver/azurerm_spec.rb b/spec/unit/kitchen/driver/azurerm_spec.rb new file mode 100644 index 0000000..f53c63f --- /dev/null +++ b/spec/unit/kitchen/driver/azurerm_spec.rb @@ -0,0 +1,119 @@ +require "spec_helper" +require "kitchen/transport/dummy" + +describe Kitchen::Driver::Azurerm do + let(:logged_output) { StringIO.new } + let(:logger) { Logger.new(logged_output) } + let(:platform) { Kitchen::Platform.new(name: "fake_platform") } + let(:transport) { Kitchen::Transport::Dummy.new } + let(:driver) { described_class.new(config) } + + let(:subscription_id) { "115b12cb-b0d3-4ed9-94db-f73733be6f3c" } + let(:location) { "eastus2" } + let(:machine_size) { "Standard_D4_v3" } + let(:vm_tags) do + { + os_type: "linux", + distro: "redhat", + } + end + + let(:image_urn) { "RedHat:rhel-byos:rhel-raw76:7.6.20190620" } + let(:vm_name) { "my-awesome-vm" } + + let(:config) do + { + subscription_id: subscription_id, + location: location, + machine_size: machine_size, + vm_tags: vm_tags, + image_urn: image_urn, + vm_name: vm_name, + } + end + + let(:instance) do + instance_double(Kitchen::Instance, + logger: logger, + transport: transport, + platform: platform, + to_str: "instance_str") + end + + before do + allow(driver).to receive(:instance).and_return(instance) + end + + it "driver API version is 2" do + expect(driver.diagnose_plugin[:api_version]).to eq(2) + end + + describe "#name" do + it "has an overridden name" do + expect(driver.name).to eq("Azurerm") + end + end + + describe "#create" do + end + + describe "#virtual_machine_deployment_template" do + subject { driver.send(:virtual_machine_deployment_template) } + + let(:parsed_json) { JSON.parse(subject) } + let(:vm_resource) { parsed_json["resources"].find { |x| x["type"] == "Microsoft.Compute/virtualMachines" } } + + context "when plan config is provided" do + let(:plan_name) { "plan-abc" } + let(:plan_product) { "my-product" } + let(:plan_publisher) { "captain-america" } + let(:plan_promotion_code) { "50-percent-off" } + + let(:plan) do + { + name: plan_name, + product: plan_product, + publisher: plan_publisher, + promotion_code: plan_promotion_code, + } + end + + let(:config) do + { + subscription_id: subscription_id, + location: location, + machine_size: machine_size, + vm_tags: vm_tags, + plan: plan, + image_urn: image_urn, + vm_name: vm_name, + } + end + + it "includes plan information in deployment template" do + expect(vm_resource).to have_key("plan") + expect(vm_resource["plan"]["name"]).to eq(plan_name) + expect(vm_resource["plan"]["product"]).to eq(plan_product) + expect(vm_resource["plan"]["publisher"]).to eq(plan_publisher) + expect(vm_resource["plan"]["promotionCode"]).to eq(plan_promotion_code) + end + end + + context "when plan config is not provided" do + let(:config) do + { + subscription_id: subscription_id, + location: location, + machine_size: machine_size, + vm_tags: vm_tags, + image_urn: image_urn, + vm_name: vm_name, + } + end + + it "does not include plan information in deployment template" do + expect(vm_resource).not_to have_key("plan") + end + end + end +end