From 7d07fa7afcfa4298975db0ea71f0489ac1ac7991 Mon Sep 17 00:00:00 2001 From: Christopher Boumenot Date: Thu, 13 Oct 2016 11:56:23 -0700 Subject: [PATCH] azure: Allow user to set custom data --- builder/azure/arm/config.go | 21 +++ builder/azure/arm/config_test.go | 23 +++ builder/azure/arm/template_factory.go | 4 + ...stVirtualMachineDeployment07.approved.json | 160 ++++++++++++++++++ builder/azure/arm/template_factory_test.go | 46 +++++ .../azure/common/template/template_builder.go | 12 ++ 6 files changed, 266 insertions(+) create mode 100644 builder/azure/arm/template_factory_test.TestVirtualMachineDeployment07.approved.json diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 68638dc3f..fa7b7d4da 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -79,6 +79,8 @@ type Config struct { VirtualNetworkName string `mapstructure:"virtual_network_name"` VirtualNetworkSubnetName string `mapstructure:"virtual_network_subnet_name"` VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"` + CustomDataFile string `mapstructure:"custom_data_file"` + customData string // OS OSType string `mapstructure:"os_type"` @@ -203,6 +205,11 @@ func newConfig(raws ...interface{}) (*Config, []string, error) { return nil, nil, err } + err = setCustomData(&c) + if err != nil { + return nil, nil, err + } + // NOTE: if the user did not specify a communicator, then default to both // SSH and WinRM. This is for backwards compatibility because the code did // not specifically force the user to set a communicator. @@ -338,6 +345,20 @@ func setCloudEnvironment(c *Config) error { return err } +func setCustomData(c *Config) error { + if c.CustomDataFile == "" { + return nil + } + + b, err := ioutil.ReadFile(c.CustomDataFile) + if err != nil { + return err + } + + c.customData = base64.StdEncoding.EncodeToString(b) + return nil +} + func provideDefaultValues(c *Config) { if c.VMSize == "" { c.VMSize = DefaultVMSize diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index f54f10c2c..79daadcc2 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -673,6 +673,29 @@ func TestConfigShouldRejectExcessiveTagValueLength(t *testing.T) { } } +func TestConfigShouldRejectMissingCustomDataFile(t *testing.T) { + config := map[string]interface{}{ + "capture_name_prefix": "ignore", + "capture_container_name": "ignore", + "image_offer": "ignore", + "image_publisher": "ignore", + "image_sku": "ignore", + "location": "ignore", + "storage_account": "ignore", + "resource_group_name": "ignore", + "subscription_id": "ignore", + "communicator": "none", + // Does not matter for this test case, just pick one. + "os_type": constants.Target_Linux, + "custom_data_file": "/this/file/does/not/exist", + } + + _, _, err := newConfig(config, getPackerConfiguration()) + if err == nil { + t.Fatal("expected config to reject missing custom data file") + } +} + func getArmBuilderConfiguration() map[string]string { m := make(map[string]string) for _, v := range requiredConfigValues { diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go index f4be38527..ba2e4ebc6 100644 --- a/builder/azure/arm/template_factory.go +++ b/builder/azure/arm/template_factory.go @@ -59,6 +59,10 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) builder.SetOSDiskSizeGB(config.OSDiskSizeGB) } + if config.customData != "" { + builder.SetCustomData(config.customData) + } + if config.VirtualNetworkName != "" { builder.SetVirtualNetwork( config.VirtualNetworkResourceGroupName, diff --git a/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment07.approved.json b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment07.approved.json new file mode 100644 index 000000000..02bac74f3 --- /dev/null +++ b/builder/azure/arm/template_factory_test.TestVirtualMachineDeployment07.approved.json @@ -0,0 +1,160 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": { + "adminPassword": { + "type": "string" + }, + "adminUsername": { + "type": "string" + }, + "dnsNameForPublicIP": { + "type": "string" + }, + "osDiskName": { + "type": "string" + }, + "storageAccountBlobEndpoint": { + "type": "string" + }, + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "[variables('apiVersion')]", + "location": "[variables('location')]", + "name": "[variables('publicIPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[parameters('dnsNameForPublicIP')]" + }, + "publicIPAllocationMethod": "[variables('publicIPAddressType')]" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('apiVersion')]", + "location": "[variables('location')]", + "name": "[variables('virtualNetworkName')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnetName')]", + "properties": { + "addressPrefix": "[variables('subnetAddressPrefix')]" + } + } + ] + }, + "type": "Microsoft.Network/virtualNetworks" + }, + { + "apiVersion": "[variables('apiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", + "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" + ], + "location": "[variables('location')]", + "name": "[variables('nicName')]", + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('apiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('vmName')]", + "properties": { + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": false + } + }, + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" + } + ] + }, + "osProfile": { + "adminPassword": "[parameters('adminPassword')]", + "adminUsername": "[parameters('adminUsername')]", + "computerName": "[parameters('vmName')]", + "customData": "I2Nsb3VkLWNvbmZpZwpncm93cGFydDoKICBtb2RlOiBvZmYK", + "linuxConfiguration": { + "ssh": { + "publicKeys": [ + { + "keyData": "", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "image": { + "uri": "https://localhost/custom.vhd" + }, + "name": "osdisk", + "osType": "Linux", + "vhd": { + "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" + } + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + } + ], + "variables": { + "addressPrefix": "10.0.0.0/16", + "apiVersion": "2015-06-15", + "location": "[resourceGroup().location]", + "nicName": "packerNic", + "publicIPAddressName": "packerPublicIP", + "publicIPAddressType": "Dynamic", + "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", + "subnetAddressPrefix": "10.0.0.0/24", + "subnetName": "packerSubnet", + "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", + "virtualNetworkName": "packerNetwork", + "virtualNetworkResourceGroup": "[resourceGroup().name]", + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } +} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.go b/builder/azure/arm/template_factory_test.go index 0e78a86ab..e27669435 100644 --- a/builder/azure/arm/template_factory_test.go +++ b/builder/azure/arm/template_factory_test.go @@ -1,6 +1,7 @@ package arm import ( + "encoding/base64" "encoding/json" "testing" @@ -212,6 +213,51 @@ func TestVirtualMachineDeployment06(t *testing.T) { } } +// Verify that custom data are properly inserted +func TestVirtualMachineDeployment07(t *testing.T) { + config := map[string]interface{}{ + "capture_name_prefix": "ignore", + "capture_container_name": "ignore", + "location": "ignore", + "image_url": "https://localhost/custom.vhd", + "resource_group_name": "ignore", + "storage_account": "ignore", + "subscription_id": "ignore", + "os_type": constants.Target_Linux, + "communicator": "none", + } + + c, _, err := newConfig(config, getPackerConfiguration()) + if err != nil { + t.Fatal(err) + } + + // The user specifies a configuration value for the setting custom_data_file. + // The config type will read that file, and base64 encode it. The encoded + // contents are then assigned to Config's customData property, which are directly + // injected into the template. + // + // I am not aware of an easy to mimic this situation in a test without having + // a file on disk, which I am loathe to do. The alternative is to inject base64 + // encoded data myself, which is what I am doing here. + customData := `#cloud-config +growpart: + mode: off +` + base64CustomData := base64.StdEncoding.EncodeToString([]byte(customData)) + c.customData = base64CustomData + + deployment, err := GetVirtualMachineDeployment(c) + if err != nil { + t.Fatal(err) + } + + err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) + if err != nil { + t.Fatal(err) + } +} + // Ensure the link values are not set, and the concrete values are set. func TestKeyVaultDeployment00(t *testing.T) { c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go index 85b82aa66..e338f3eb4 100644 --- a/builder/azure/common/template/template_builder.go +++ b/builder/azure/common/template/template_builder.go @@ -140,6 +140,18 @@ func (s *TemplateBuilder) SetOSDiskSizeGB(diskSizeGB int32) error { return nil } +func (s *TemplateBuilder) SetCustomData(customData string) error { + resource, err := s.getResourceByType(resourceVirtualMachine) + if err != nil { + return err + } + + profile := resource.Properties.OsProfile + profile.CustomData = to.StringPtr(customData) + + return nil +} + func (s *TemplateBuilder) SetVirtualNetwork(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error { s.setVariable("virtualNetworkResourceGroup", virtualNetworkResourceGroup) s.setVariable("virtualNetworkName", virtualNetworkName)