Extract Azure plugin components from Packer (#10979)
* Remove Azure plugin components and docs * Add Azure plugin to vendored_plugins * Updates Azure to use remote plugin docs * Revendor packer-plugin-azure at v0.0.2 * Update to new version of the Packer plugin SDK v0.2.1 * Update to the official version of go-getter@v2.0.0 * Update salt provisioner to use new go-getter API * Update vendored plugins to working versions This changes fixes an issue with the go.sum for the Azure plugin. It also revendors the plugins for puppet, chef, and ansible as v0.0.1 of those plugins where not usable.
This commit is contained in:
parent
a8db14ab6b
commit
c262467413
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) Microsoft Corporation
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
|
@ -1,16 +0,0 @@
|
||||||
Here's a list of things we like to get done in no particular order:
|
|
||||||
|
|
||||||
- [ ] Blob/image copy post-processor
|
|
||||||
- [ ] Blob/image rename post-processor
|
|
||||||
- [ ] SSH to private ip through subnet
|
|
||||||
- [ ] chroot builder
|
|
||||||
- [ ] support cross-storage account image source (i.e. pre-build blob copy)
|
|
||||||
- [ ] look up object id when using device code (graph api /me ?)
|
|
||||||
- [ ] device flow support for Windows
|
|
||||||
- [x] look up tenant id in all cases (see device flow code)
|
|
||||||
- [ ] look up resource group of storage account
|
|
||||||
- [ ] include all _data_ disks in artifact too
|
|
||||||
- [ ] windows sysprep provisioner (since it seems to generate a certain issue volume)
|
|
||||||
- [ ] allow arbitrary json patching for deployment document
|
|
||||||
- [ ] tag all resources with user-supplied tag
|
|
||||||
- [ ] managed disk support
|
|
|
@ -1,118 +0,0 @@
|
||||||
{
|
|
||||||
"$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')]",
|
|
||||||
"dependsOn": [],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[variables('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"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')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"image": {
|
|
||||||
"uri": "https://localhost/custom.vhd"
|
|
||||||
},
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"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]",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "ignore",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "ignore",
|
|
||||||
"virtualNetworkResourceGroup": "ignore",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,261 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
BuilderId = "Azure.ResourceManagement.VMImage"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AdditionalDiskArtifact struct {
|
|
||||||
AdditionalDiskUri string
|
|
||||||
AdditionalDiskUriReadOnlySas string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Artifact struct {
|
|
||||||
// OS type: Linux, Windows
|
|
||||||
OSType string
|
|
||||||
|
|
||||||
// VHD
|
|
||||||
StorageAccountLocation string
|
|
||||||
OSDiskUri string
|
|
||||||
TemplateUri string
|
|
||||||
OSDiskUriReadOnlySas string
|
|
||||||
TemplateUriReadOnlySas string
|
|
||||||
|
|
||||||
// Managed Image
|
|
||||||
ManagedImageResourceGroupName string
|
|
||||||
ManagedImageName string
|
|
||||||
ManagedImageLocation string
|
|
||||||
ManagedImageId string
|
|
||||||
ManagedImageOSDiskSnapshotName string
|
|
||||||
ManagedImageDataDiskSnapshotPrefix string
|
|
||||||
// ARM resource id for Shared Image Gallery
|
|
||||||
ManagedImageSharedImageGalleryId string
|
|
||||||
|
|
||||||
// Additional Disks
|
|
||||||
AdditionalDisks *[]AdditionalDiskArtifact
|
|
||||||
|
|
||||||
// StateData should store data such as GeneratedData
|
|
||||||
// to be shared with post-processors
|
|
||||||
StateData map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewManagedImageArtifact(osType, resourceGroup, name, location, id, osDiskSnapshotName, dataDiskSnapshotPrefix string, generatedData map[string]interface{}, keepOSDisk bool, template *CaptureTemplate, getSasUrl func(name string) string) (*Artifact, error) {
|
|
||||||
res := Artifact{
|
|
||||||
ManagedImageResourceGroupName: resourceGroup,
|
|
||||||
ManagedImageName: name,
|
|
||||||
ManagedImageLocation: location,
|
|
||||||
ManagedImageId: id,
|
|
||||||
OSType: osType,
|
|
||||||
ManagedImageOSDiskSnapshotName: osDiskSnapshotName,
|
|
||||||
ManagedImageDataDiskSnapshotPrefix: dataDiskSnapshotPrefix,
|
|
||||||
StateData: generatedData,
|
|
||||||
}
|
|
||||||
|
|
||||||
if keepOSDisk {
|
|
||||||
if template == nil {
|
|
||||||
log.Printf("artifact error: nil capture template")
|
|
||||||
return &res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(template.Resources) != 1 {
|
|
||||||
log.Printf("artifact error: malformed capture template, expected one resource")
|
|
||||||
return &res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
vhdUri, err := url.Parse(template.Resources[0].Properties.StorageProfile.OSDisk.Image.Uri)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("artifact error: Error parsing osdisk url: %s", err)
|
|
||||||
return &res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
res.OSDiskUri = vhdUri.String()
|
|
||||||
res.OSDiskUriReadOnlySas = getSasUrl(getStorageUrlPath(vhdUri))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewManagedImageArtifactWithSIGAsDestination(osType, resourceGroup, name, location, id, osDiskSnapshotName, dataDiskSnapshotPrefix, destinationSharedImageGalleryId string, generatedData map[string]interface{}) (*Artifact, error) {
|
|
||||||
return &Artifact{
|
|
||||||
ManagedImageResourceGroupName: resourceGroup,
|
|
||||||
ManagedImageName: name,
|
|
||||||
ManagedImageLocation: location,
|
|
||||||
ManagedImageId: id,
|
|
||||||
OSType: osType,
|
|
||||||
ManagedImageOSDiskSnapshotName: osDiskSnapshotName,
|
|
||||||
ManagedImageDataDiskSnapshotPrefix: dataDiskSnapshotPrefix,
|
|
||||||
ManagedImageSharedImageGalleryId: destinationSharedImageGalleryId,
|
|
||||||
StateData: generatedData,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewArtifact(template *CaptureTemplate, getSasUrl func(name string) string, osType string, generatedData map[string]interface{}) (*Artifact, error) {
|
|
||||||
if template == nil {
|
|
||||||
return nil, fmt.Errorf("nil capture template")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(template.Resources) != 1 {
|
|
||||||
return nil, fmt.Errorf("malformed capture template, expected one resource")
|
|
||||||
}
|
|
||||||
|
|
||||||
vhdUri, err := url.Parse(template.Resources[0].Properties.StorageProfile.OSDisk.Image.Uri)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
templateUri, err := storageUriToTemplateUri(vhdUri)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var additional_disks *[]AdditionalDiskArtifact
|
|
||||||
if template.Resources[0].Properties.StorageProfile.DataDisks != nil {
|
|
||||||
data_disks := make([]AdditionalDiskArtifact, len(template.Resources[0].Properties.StorageProfile.DataDisks))
|
|
||||||
for i, additionaldisk := range template.Resources[0].Properties.StorageProfile.DataDisks {
|
|
||||||
additionalVhdUri, err := url.Parse(additionaldisk.Image.Uri)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data_disks[i].AdditionalDiskUri = additionalVhdUri.String()
|
|
||||||
data_disks[i].AdditionalDiskUriReadOnlySas = getSasUrl(getStorageUrlPath(additionalVhdUri))
|
|
||||||
}
|
|
||||||
additional_disks = &data_disks
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Artifact{
|
|
||||||
OSType: osType,
|
|
||||||
OSDiskUri: vhdUri.String(),
|
|
||||||
OSDiskUriReadOnlySas: getSasUrl(getStorageUrlPath(vhdUri)),
|
|
||||||
TemplateUri: templateUri.String(),
|
|
||||||
TemplateUriReadOnlySas: getSasUrl(getStorageUrlPath(templateUri)),
|
|
||||||
|
|
||||||
AdditionalDisks: additional_disks,
|
|
||||||
|
|
||||||
StorageAccountLocation: template.Resources[0].Location,
|
|
||||||
|
|
||||||
StateData: generatedData,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStorageUrlPath(u *url.URL) string {
|
|
||||||
parts := strings.Split(u.Path, "/")
|
|
||||||
return strings.Join(parts[3:], "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
func storageUriToTemplateUri(su *url.URL) (*url.URL, error) {
|
|
||||||
// packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd -> 4085bb15-3644-4641-b9cd-f575918640b4
|
|
||||||
filename := path.Base(su.Path)
|
|
||||||
parts := strings.Split(filename, ".")
|
|
||||||
|
|
||||||
if len(parts) < 3 {
|
|
||||||
return nil, fmt.Errorf("malformed URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
// packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd -> packer
|
|
||||||
prefixParts := strings.Split(parts[0], "-")
|
|
||||||
prefix := strings.Join(prefixParts[:len(prefixParts)-1], "-")
|
|
||||||
|
|
||||||
templateFilename := fmt.Sprintf("%s-vmTemplate.%s.json", prefix, parts[1])
|
|
||||||
|
|
||||||
// https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd"
|
|
||||||
// ->
|
|
||||||
// https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json"
|
|
||||||
return url.Parse(strings.Replace(su.String(), filename, templateFilename, 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Artifact) isManagedImage() bool {
|
|
||||||
return a.ManagedImageResourceGroupName != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Artifact) BuilderId() string {
|
|
||||||
return BuilderId
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Artifact) Files() []string {
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Artifact) Id() string {
|
|
||||||
if a.OSDiskUri != "" {
|
|
||||||
return a.OSDiskUri
|
|
||||||
}
|
|
||||||
return a.ManagedImageId
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Artifact) State(name string) interface{} {
|
|
||||||
if _, ok := a.StateData[name]; ok {
|
|
||||||
return a.StateData[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
switch name {
|
|
||||||
case "atlas.artifact.metadata":
|
|
||||||
return a.stateAtlasMetadata()
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Artifact) String() string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
buf.WriteString(fmt.Sprintf("%s:\n\n", a.BuilderId()))
|
|
||||||
buf.WriteString(fmt.Sprintf("OSType: %s\n", a.OSType))
|
|
||||||
if a.isManagedImage() {
|
|
||||||
buf.WriteString(fmt.Sprintf("ManagedImageResourceGroupName: %s\n", a.ManagedImageResourceGroupName))
|
|
||||||
buf.WriteString(fmt.Sprintf("ManagedImageName: %s\n", a.ManagedImageName))
|
|
||||||
buf.WriteString(fmt.Sprintf("ManagedImageId: %s\n", a.ManagedImageId))
|
|
||||||
buf.WriteString(fmt.Sprintf("ManagedImageLocation: %s\n", a.ManagedImageLocation))
|
|
||||||
if a.ManagedImageOSDiskSnapshotName != "" {
|
|
||||||
buf.WriteString(fmt.Sprintf("ManagedImageOSDiskSnapshotName: %s\n", a.ManagedImageOSDiskSnapshotName))
|
|
||||||
}
|
|
||||||
if a.ManagedImageDataDiskSnapshotPrefix != "" {
|
|
||||||
buf.WriteString(fmt.Sprintf("ManagedImageDataDiskSnapshotPrefix: %s\n", a.ManagedImageDataDiskSnapshotPrefix))
|
|
||||||
}
|
|
||||||
if a.ManagedImageSharedImageGalleryId != "" {
|
|
||||||
buf.WriteString(fmt.Sprintf("ManagedImageSharedImageGalleryId: %s\n", a.ManagedImageSharedImageGalleryId))
|
|
||||||
}
|
|
||||||
if a.OSDiskUri != "" {
|
|
||||||
buf.WriteString(fmt.Sprintf("OSDiskUri: %s\n", a.OSDiskUri))
|
|
||||||
}
|
|
||||||
if a.OSDiskUriReadOnlySas != "" {
|
|
||||||
buf.WriteString(fmt.Sprintf("OSDiskUriReadOnlySas: %s\n", a.OSDiskUriReadOnlySas))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buf.WriteString(fmt.Sprintf("StorageAccountLocation: %s\n", a.StorageAccountLocation))
|
|
||||||
buf.WriteString(fmt.Sprintf("OSDiskUri: %s\n", a.OSDiskUri))
|
|
||||||
buf.WriteString(fmt.Sprintf("OSDiskUriReadOnlySas: %s\n", a.OSDiskUriReadOnlySas))
|
|
||||||
buf.WriteString(fmt.Sprintf("TemplateUri: %s\n", a.TemplateUri))
|
|
||||||
buf.WriteString(fmt.Sprintf("TemplateUriReadOnlySas: %s\n", a.TemplateUriReadOnlySas))
|
|
||||||
if a.AdditionalDisks != nil {
|
|
||||||
for i, additionaldisk := range *a.AdditionalDisks {
|
|
||||||
buf.WriteString(fmt.Sprintf("AdditionalDiskUri (datadisk-%d): %s\n", i+1, additionaldisk.AdditionalDiskUri))
|
|
||||||
buf.WriteString(fmt.Sprintf("AdditionalDiskUriReadOnlySas (datadisk-%d): %s\n", i+1, additionaldisk.AdditionalDiskUriReadOnlySas))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Artifact) Destroy() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Artifact) stateAtlasMetadata() interface{} {
|
|
||||||
metadata := make(map[string]string)
|
|
||||||
metadata["StorageAccountLocation"] = a.StorageAccountLocation
|
|
||||||
metadata["OSDiskUri"] = a.OSDiskUri
|
|
||||||
metadata["OSDiskUriReadOnlySas"] = a.OSDiskUriReadOnlySas
|
|
||||||
metadata["TemplateUri"] = a.TemplateUri
|
|
||||||
metadata["TemplateUriReadOnlySas"] = a.TemplateUriReadOnlySas
|
|
||||||
|
|
||||||
return metadata
|
|
||||||
}
|
|
|
@ -1,521 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getFakeSasUrl(name string) string {
|
|
||||||
return fmt.Sprintf("SAS-%s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func generatedData() map[string]interface{} {
|
|
||||||
return make(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactIdVHD(t *testing.T) {
|
|
||||||
template := CaptureTemplate{
|
|
||||||
Resources: []CaptureResources{
|
|
||||||
{
|
|
||||||
Properties: CaptureProperties{
|
|
||||||
StorageProfile: CaptureStorageProfile{
|
|
||||||
OSDisk: CaptureDisk{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: "southcentralus",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err=%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd"
|
|
||||||
|
|
||||||
result := artifact.Id()
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("bad: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactIDManagedImage(t *testing.T) {
|
|
||||||
template := CaptureTemplate{
|
|
||||||
Resources: []CaptureResources{
|
|
||||||
{
|
|
||||||
Properties: CaptureProperties{
|
|
||||||
StorageProfile: CaptureStorageProfile{
|
|
||||||
OSDisk: CaptureDisk{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: "southcentralus",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "fakeDataDiskSnapshotPrefix", generatedData(), false, &template, getFakeSasUrl)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err=%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := `Azure.ResourceManagement.VMImage:
|
|
||||||
|
|
||||||
OSType: Linux
|
|
||||||
ManagedImageResourceGroupName: fakeResourceGroup
|
|
||||||
ManagedImageName: fakeName
|
|
||||||
ManagedImageId: fakeID
|
|
||||||
ManagedImageLocation: fakeLocation
|
|
||||||
ManagedImageOSDiskSnapshotName: fakeOsDiskSnapshotName
|
|
||||||
ManagedImageDataDiskSnapshotPrefix: fakeDataDiskSnapshotPrefix
|
|
||||||
`
|
|
||||||
|
|
||||||
result := artifact.String()
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("bad: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactIDManagedImageWithoutOSDiskSnapshotName(t *testing.T) {
|
|
||||||
template := CaptureTemplate{
|
|
||||||
Resources: []CaptureResources{
|
|
||||||
{
|
|
||||||
Properties: CaptureProperties{
|
|
||||||
StorageProfile: CaptureStorageProfile{
|
|
||||||
OSDisk: CaptureDisk{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: "southcentralus",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "", "fakeDataDiskSnapshotPrefix", generatedData(), false, &template, getFakeSasUrl)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err=%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := `Azure.ResourceManagement.VMImage:
|
|
||||||
|
|
||||||
OSType: Linux
|
|
||||||
ManagedImageResourceGroupName: fakeResourceGroup
|
|
||||||
ManagedImageName: fakeName
|
|
||||||
ManagedImageId: fakeID
|
|
||||||
ManagedImageLocation: fakeLocation
|
|
||||||
ManagedImageDataDiskSnapshotPrefix: fakeDataDiskSnapshotPrefix
|
|
||||||
`
|
|
||||||
|
|
||||||
result := artifact.String()
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("bad: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactIDManagedImageWithoutDataDiskSnapshotPrefix(t *testing.T) {
|
|
||||||
template := CaptureTemplate{
|
|
||||||
Resources: []CaptureResources{
|
|
||||||
{
|
|
||||||
Properties: CaptureProperties{
|
|
||||||
StorageProfile: CaptureStorageProfile{
|
|
||||||
OSDisk: CaptureDisk{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: "southcentralus",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "", generatedData(), false, &template, getFakeSasUrl)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err=%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := `Azure.ResourceManagement.VMImage:
|
|
||||||
|
|
||||||
OSType: Linux
|
|
||||||
ManagedImageResourceGroupName: fakeResourceGroup
|
|
||||||
ManagedImageName: fakeName
|
|
||||||
ManagedImageId: fakeID
|
|
||||||
ManagedImageLocation: fakeLocation
|
|
||||||
ManagedImageOSDiskSnapshotName: fakeOsDiskSnapshotName
|
|
||||||
`
|
|
||||||
|
|
||||||
result := artifact.String()
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("bad: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactIDManagedImageWithKeepingTheOSDisk(t *testing.T) {
|
|
||||||
template := CaptureTemplate{
|
|
||||||
Resources: []CaptureResources{
|
|
||||||
{
|
|
||||||
Properties: CaptureProperties{
|
|
||||||
StorageProfile: CaptureStorageProfile{
|
|
||||||
OSDisk: CaptureDisk{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: "southcentralus",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
artifact, err := NewManagedImageArtifact("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "", generatedData(), true, &template, getFakeSasUrl)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err=%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := `Azure.ResourceManagement.VMImage:
|
|
||||||
|
|
||||||
OSType: Linux
|
|
||||||
ManagedImageResourceGroupName: fakeResourceGroup
|
|
||||||
ManagedImageName: fakeName
|
|
||||||
ManagedImageId: fakeID
|
|
||||||
ManagedImageLocation: fakeLocation
|
|
||||||
ManagedImageOSDiskSnapshotName: fakeOsDiskSnapshotName
|
|
||||||
OSDiskUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd
|
|
||||||
OSDiskUriReadOnlySas: SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd
|
|
||||||
`
|
|
||||||
|
|
||||||
result := artifact.String()
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("bad: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactIDManagedImageWithSharedImageGalleryId(t *testing.T) {
|
|
||||||
artifact, err := NewManagedImageArtifactWithSIGAsDestination("Linux", "fakeResourceGroup", "fakeName", "fakeLocation", "fakeID", "fakeOsDiskSnapshotName", "fakeDataDiskSnapshotPrefix", "fakeSharedImageGallery", generatedData())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err=%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := `Azure.ResourceManagement.VMImage:
|
|
||||||
|
|
||||||
OSType: Linux
|
|
||||||
ManagedImageResourceGroupName: fakeResourceGroup
|
|
||||||
ManagedImageName: fakeName
|
|
||||||
ManagedImageId: fakeID
|
|
||||||
ManagedImageLocation: fakeLocation
|
|
||||||
ManagedImageOSDiskSnapshotName: fakeOsDiskSnapshotName
|
|
||||||
ManagedImageDataDiskSnapshotPrefix: fakeDataDiskSnapshotPrefix
|
|
||||||
ManagedImageSharedImageGalleryId: fakeSharedImageGallery
|
|
||||||
`
|
|
||||||
|
|
||||||
result := artifact.String()
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("bad: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactString(t *testing.T) {
|
|
||||||
template := CaptureTemplate{
|
|
||||||
Resources: []CaptureResources{
|
|
||||||
{
|
|
||||||
Properties: CaptureProperties{
|
|
||||||
StorageProfile: CaptureStorageProfile{
|
|
||||||
OSDisk: CaptureDisk{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: "southcentralus",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err=%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testSubject := artifact.String()
|
|
||||||
if !strings.Contains(testSubject, "OSDiskUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd") {
|
|
||||||
t.Errorf("Expected String() output to contain OSDiskUri")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "OSDiskUriReadOnlySas: SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd") {
|
|
||||||
t.Errorf("Expected String() output to contain OSDiskUriReadOnlySas")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "TemplateUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json") {
|
|
||||||
t.Errorf("Expected String() output to contain TemplateUri")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "TemplateUriReadOnlySas: SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json") {
|
|
||||||
t.Errorf("Expected String() output to contain TemplateUriReadOnlySas")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "StorageAccountLocation: southcentralus") {
|
|
||||||
t.Errorf("Expected String() output to contain StorageAccountLocation")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "OSType: Linux") {
|
|
||||||
t.Errorf("Expected String() output to contain OSType")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAdditionalDiskArtifactString(t *testing.T) {
|
|
||||||
template := CaptureTemplate{
|
|
||||||
Resources: []CaptureResources{
|
|
||||||
{
|
|
||||||
Properties: CaptureProperties{
|
|
||||||
StorageProfile: CaptureStorageProfile{
|
|
||||||
OSDisk: CaptureDisk{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
DataDisks: []CaptureDisk{
|
|
||||||
{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: "southcentralus",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err=%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testSubject := artifact.String()
|
|
||||||
if !strings.Contains(testSubject, "OSDiskUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd") {
|
|
||||||
t.Errorf("Expected String() output to contain OSDiskUri")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "OSDiskUriReadOnlySas: SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd") {
|
|
||||||
t.Errorf("Expected String() output to contain OSDiskUriReadOnlySas")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "TemplateUri: https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json") {
|
|
||||||
t.Errorf("Expected String() output to contain TemplateUri")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "TemplateUriReadOnlySas: SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json") {
|
|
||||||
t.Errorf("Expected String() output to contain TemplateUriReadOnlySas")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "StorageAccountLocation: southcentralus") {
|
|
||||||
t.Errorf("Expected String() output to contain StorageAccountLocation")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "OSType: Linux") {
|
|
||||||
t.Errorf("Expected String() output to contain OSType")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "AdditionalDiskUri (datadisk-1): https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd") {
|
|
||||||
t.Errorf("Expected String() output to contain AdditionalDiskUri")
|
|
||||||
}
|
|
||||||
if !strings.Contains(testSubject, "AdditionalDiskUriReadOnlySas (datadisk-1): SAS-Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd") {
|
|
||||||
t.Errorf("Expected String() output to contain AdditionalDiskUriReadOnlySas")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactProperties(t *testing.T) {
|
|
||||||
template := CaptureTemplate{
|
|
||||||
Resources: []CaptureResources{
|
|
||||||
{
|
|
||||||
Properties: CaptureProperties{
|
|
||||||
StorageProfile: CaptureStorageProfile{
|
|
||||||
OSDisk: CaptureDisk{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: "southcentralus",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err=%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if testSubject.OSDiskUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" {
|
|
||||||
t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", testSubject.OSDiskUri)
|
|
||||||
}
|
|
||||||
if testSubject.OSDiskUriReadOnlySas != "SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" {
|
|
||||||
t.Errorf("Expected template to be 'SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", testSubject.OSDiskUriReadOnlySas)
|
|
||||||
}
|
|
||||||
if testSubject.TemplateUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" {
|
|
||||||
t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUri)
|
|
||||||
}
|
|
||||||
if testSubject.TemplateUriReadOnlySas != "SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" {
|
|
||||||
t.Errorf("Expected template to be 'SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUriReadOnlySas)
|
|
||||||
}
|
|
||||||
if testSubject.StorageAccountLocation != "southcentralus" {
|
|
||||||
t.Errorf("Expected StorageAccountLocation to be 'southcentral', but got %s", testSubject.StorageAccountLocation)
|
|
||||||
}
|
|
||||||
if testSubject.OSType != "Linux" {
|
|
||||||
t.Errorf("Expected OSType to be 'Linux', but got %s", testSubject.OSType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAdditionalDiskArtifactProperties(t *testing.T) {
|
|
||||||
template := CaptureTemplate{
|
|
||||||
Resources: []CaptureResources{
|
|
||||||
{
|
|
||||||
Properties: CaptureProperties{
|
|
||||||
StorageProfile: CaptureStorageProfile{
|
|
||||||
OSDisk: CaptureDisk{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
DataDisks: []CaptureDisk{
|
|
||||||
{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: "southcentralus",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err=%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if testSubject.OSDiskUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" {
|
|
||||||
t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", testSubject.OSDiskUri)
|
|
||||||
}
|
|
||||||
if testSubject.OSDiskUriReadOnlySas != "SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd" {
|
|
||||||
t.Errorf("Expected template to be 'SAS-Images/images/packer-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", testSubject.OSDiskUriReadOnlySas)
|
|
||||||
}
|
|
||||||
if testSubject.TemplateUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" {
|
|
||||||
t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUri)
|
|
||||||
}
|
|
||||||
if testSubject.TemplateUriReadOnlySas != "SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" {
|
|
||||||
t.Errorf("Expected template to be 'SAS-Images/images/packer-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUriReadOnlySas)
|
|
||||||
}
|
|
||||||
if testSubject.StorageAccountLocation != "southcentralus" {
|
|
||||||
t.Errorf("Expected StorageAccountLocation to be 'southcentral', but got %s", testSubject.StorageAccountLocation)
|
|
||||||
}
|
|
||||||
if testSubject.OSType != "Linux" {
|
|
||||||
t.Errorf("Expected OSType to be 'Linux', but got %s", testSubject.OSType)
|
|
||||||
}
|
|
||||||
if testSubject.AdditionalDisks == nil {
|
|
||||||
t.Errorf("Expected AdditionalDisks to be not nil")
|
|
||||||
}
|
|
||||||
if len(*testSubject.AdditionalDisks) != 1 {
|
|
||||||
t.Errorf("Expected AdditionalDisks to have one additional disk, but got %d", len(*testSubject.AdditionalDisks))
|
|
||||||
}
|
|
||||||
if (*testSubject.AdditionalDisks)[0].AdditionalDiskUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd" {
|
|
||||||
t.Errorf("Expected additional disk uri to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", (*testSubject.AdditionalDisks)[0].AdditionalDiskUri)
|
|
||||||
}
|
|
||||||
if (*testSubject.AdditionalDisks)[0].AdditionalDiskUriReadOnlySas != "SAS-Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd" {
|
|
||||||
t.Errorf("Expected additional disk sas to be 'SAS-Images/images/packer-datadisk-1.4085bb15-3644-4641-b9cd-f575918640b4.vhd', but got %s", (*testSubject.AdditionalDisks)[0].AdditionalDiskUriReadOnlySas)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactOverHyphenatedCaptureUri(t *testing.T) {
|
|
||||||
template := CaptureTemplate{
|
|
||||||
Resources: []CaptureResources{
|
|
||||||
{
|
|
||||||
Properties: CaptureProperties{
|
|
||||||
StorageProfile: CaptureStorageProfile{
|
|
||||||
OSDisk: CaptureDisk{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/pac-ker-osDisk.4085bb15-3644-4641-b9cd-f575918640b4.vhd",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: "southcentralus",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err=%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if testSubject.TemplateUri != "https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/pac-ker-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json" {
|
|
||||||
t.Errorf("Expected template to be 'https://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/pac-ker-vmTemplate.4085bb15-3644-4641-b9cd-f575918640b4.json', but got %s", testSubject.TemplateUri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactRejectMalformedTemplates(t *testing.T) {
|
|
||||||
template := CaptureTemplate{}
|
|
||||||
|
|
||||||
_, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData())
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Expected artifact creation to fail, but it succeeded.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactRejectMalformedStorageUri(t *testing.T) {
|
|
||||||
template := CaptureTemplate{
|
|
||||||
Resources: []CaptureResources{
|
|
||||||
{
|
|
||||||
Properties: CaptureProperties{
|
|
||||||
StorageProfile: CaptureStorageProfile{
|
|
||||||
OSDisk: CaptureDisk{
|
|
||||||
Image: CaptureUri{
|
|
||||||
Uri: "bark",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := NewArtifact(&template, getFakeSasUrl, "Linux", generatedData())
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Expected artifact creation to fail, but it succeeded.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactState_StateData(t *testing.T) {
|
|
||||||
expectedData := "this is the data"
|
|
||||||
artifact := &Artifact{
|
|
||||||
StateData: map[string]interface{}{"state_data": expectedData},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid state
|
|
||||||
result := artifact.State("state_data")
|
|
||||||
if result != expectedData {
|
|
||||||
t.Fatalf("Bad: State data was %s instead of %s", result, expectedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid state
|
|
||||||
result = artifact.State("invalid_key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for invalid state data name")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nil StateData should not fail and should return nil
|
|
||||||
artifact = &Artifact{}
|
|
||||||
result = artifact.State("key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for nil StateData")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,311 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
|
||||||
newCompute "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute"
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault"
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-01-01/network"
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources"
|
|
||||||
armStorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage"
|
|
||||||
"github.com/Azure/azure-sdk-for-go/storage"
|
|
||||||
"github.com/Azure/go-autorest/autorest"
|
|
||||||
"github.com/Azure/go-autorest/autorest/adal"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/useragent"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/version"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
EnvPackerLogAzureMaxLen = "PACKER_LOG_AZURE_MAXLEN"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AzureClient struct {
|
|
||||||
storage.BlobStorageClient
|
|
||||||
resources.DeploymentsClient
|
|
||||||
resources.DeploymentOperationsClient
|
|
||||||
resources.GroupsClient
|
|
||||||
network.PublicIPAddressesClient
|
|
||||||
network.InterfacesClient
|
|
||||||
network.SubnetsClient
|
|
||||||
network.VirtualNetworksClient
|
|
||||||
network.SecurityGroupsClient
|
|
||||||
compute.ImagesClient
|
|
||||||
compute.VirtualMachinesClient
|
|
||||||
common.VaultClient
|
|
||||||
armStorage.AccountsClient
|
|
||||||
compute.DisksClient
|
|
||||||
compute.SnapshotsClient
|
|
||||||
newCompute.GalleryImageVersionsClient
|
|
||||||
newCompute.GalleryImagesClient
|
|
||||||
|
|
||||||
InspectorMaxLength int
|
|
||||||
Template *CaptureTemplate
|
|
||||||
LastError azureErrorResponse
|
|
||||||
VaultClientDelete keyvault.VaultsClient
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCaptureResponse(body string) *CaptureTemplate {
|
|
||||||
var operation CaptureOperation
|
|
||||||
err := json.Unmarshal([]byte(body), &operation)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if operation.Properties != nil && operation.Properties.Output != nil {
|
|
||||||
return operation.Properties.Output
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK(chrboum): This method is a hack. It was written to work around this issue
|
|
||||||
// (https://github.com/Azure/azure-sdk-for-go/issues/307) and to an extent this
|
|
||||||
// issue (https://github.com/Azure/azure-rest-api-specs/issues/188).
|
|
||||||
//
|
|
||||||
// Capturing a VM is a long running operation that requires polling. There are
|
|
||||||
// couple different forms of polling, and the end result of a poll operation is
|
|
||||||
// discarded by the SDK. It is expected that any discarded data can be re-fetched,
|
|
||||||
// so discarding it has minimal impact. Unfortunately, there is no way to re-fetch
|
|
||||||
// the template returned by a capture call that I am aware of.
|
|
||||||
//
|
|
||||||
// If the second issue were fixed the VM ID would be included when GET'ing a VM. The
|
|
||||||
// VM ID could be used to locate the captured VHD, and captured template.
|
|
||||||
// Unfortunately, the VM ID is not included so this method cannot be used either.
|
|
||||||
//
|
|
||||||
// This code captures the template and saves it to the client (the AzureClient type).
|
|
||||||
// It expects that the capture API is called only once, or rather you only care that the
|
|
||||||
// last call's value is important because subsequent requests are not persisted. There
|
|
||||||
// is no care given to multiple threads writing this value because for our use case
|
|
||||||
// it does not matter.
|
|
||||||
func templateCapture(client *AzureClient) autorest.RespondDecorator {
|
|
||||||
return func(r autorest.Responder) autorest.Responder {
|
|
||||||
return autorest.ResponderFunc(func(resp *http.Response) error {
|
|
||||||
body, bodyString := handleBody(resp.Body, math.MaxInt64)
|
|
||||||
resp.Body = body
|
|
||||||
|
|
||||||
captureTemplate := getCaptureResponse(bodyString)
|
|
||||||
if captureTemplate != nil {
|
|
||||||
client.Template = captureTemplate
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.Respond(resp)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorCapture(client *AzureClient) autorest.RespondDecorator {
|
|
||||||
return func(r autorest.Responder) autorest.Responder {
|
|
||||||
return autorest.ResponderFunc(func(resp *http.Response) error {
|
|
||||||
body, bodyString := handleBody(resp.Body, math.MaxInt64)
|
|
||||||
resp.Body = body
|
|
||||||
|
|
||||||
errorResponse := newAzureErrorResponse(bodyString)
|
|
||||||
if errorResponse != nil {
|
|
||||||
client.LastError = *errorResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.Respond(resp)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WAITING(chrboum): I have logged https://github.com/Azure/azure-sdk-for-go/issues/311 to get this
|
|
||||||
// method included in the SDK. It has been accepted, and I'll cut over to the official way
|
|
||||||
// once it ships.
|
|
||||||
func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.RespondDecorator {
|
|
||||||
return func(r autorest.Responder) autorest.Responder {
|
|
||||||
return autorest.DecorateResponder(r, decorators...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storageAccountName string,
|
|
||||||
cloud *azure.Environment, sharedGalleryTimeout time.Duration, pollingDuration time.Duration,
|
|
||||||
servicePrincipalToken, servicePrincipalTokenVault *adal.ServicePrincipalToken) (*AzureClient, error) {
|
|
||||||
|
|
||||||
var azureClient = &AzureClient{}
|
|
||||||
|
|
||||||
maxlen := getInspectorMaxLength()
|
|
||||||
|
|
||||||
azureClient.DeploymentsClient = resources.NewDeploymentsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.DeploymentsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.DeploymentsClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.DeploymentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.DeploymentsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DeploymentsClient.UserAgent)
|
|
||||||
azureClient.DeploymentsClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.DeploymentOperationsClient = resources.NewDeploymentOperationsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.DeploymentOperationsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.DeploymentOperationsClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.DeploymentOperationsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.DeploymentOperationsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DeploymentOperationsClient.UserAgent)
|
|
||||||
azureClient.DeploymentOperationsClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.DisksClient = compute.NewDisksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.DisksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.DisksClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.DisksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.DisksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.DisksClient.UserAgent)
|
|
||||||
azureClient.DisksClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.GroupsClient = resources.NewGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.GroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.GroupsClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.GroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.GroupsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GroupsClient.UserAgent)
|
|
||||||
azureClient.GroupsClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.ImagesClient = compute.NewImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.ImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.ImagesClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.ImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.ImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.ImagesClient.UserAgent)
|
|
||||||
azureClient.ImagesClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.InterfacesClient = network.NewInterfacesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.InterfacesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.InterfacesClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.InterfacesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.InterfacesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.InterfacesClient.UserAgent)
|
|
||||||
azureClient.InterfacesClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.SubnetsClient = network.NewSubnetsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.SubnetsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.SubnetsClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.SubnetsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.SubnetsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.SubnetsClient.UserAgent)
|
|
||||||
azureClient.SubnetsClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.VirtualNetworksClient = network.NewVirtualNetworksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.VirtualNetworksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.VirtualNetworksClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.VirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.VirtualNetworksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VirtualNetworksClient.UserAgent)
|
|
||||||
azureClient.VirtualNetworksClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.SecurityGroupsClient = network.NewSecurityGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.SecurityGroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.SecurityGroupsClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.SecurityGroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.SecurityGroupsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.SecurityGroupsClient.UserAgent)
|
|
||||||
|
|
||||||
azureClient.PublicIPAddressesClient = network.NewPublicIPAddressesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.PublicIPAddressesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.PublicIPAddressesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.PublicIPAddressesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.PublicIPAddressesClient.UserAgent)
|
|
||||||
azureClient.PublicIPAddressesClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.VirtualMachinesClient = compute.NewVirtualMachinesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.VirtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient))
|
|
||||||
azureClient.VirtualMachinesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VirtualMachinesClient.UserAgent)
|
|
||||||
azureClient.VirtualMachinesClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.SnapshotsClient = compute.NewSnapshotsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.SnapshotsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.SnapshotsClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.SnapshotsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.SnapshotsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.SnapshotsClient.UserAgent)
|
|
||||||
azureClient.SnapshotsClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.AccountsClient = armStorage.NewAccountsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.AccountsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.AccountsClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.AccountsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.AccountsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.AccountsClient.UserAgent)
|
|
||||||
azureClient.AccountsClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
azureClient.GalleryImageVersionsClient = newCompute.NewGalleryImageVersionsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.GalleryImageVersionsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.GalleryImageVersionsClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.UserAgent)
|
|
||||||
azureClient.GalleryImageVersionsClient.Client.PollingDuration = sharedGalleryTimeout
|
|
||||||
if sigSubscriptionID != "" {
|
|
||||||
azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID
|
|
||||||
}
|
|
||||||
|
|
||||||
azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
|
||||||
azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.GalleryImagesClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImagesClient.UserAgent)
|
|
||||||
azureClient.GalleryImagesClient.Client.PollingDuration = pollingDuration
|
|
||||||
if sigSubscriptionID != "" {
|
|
||||||
azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID
|
|
||||||
}
|
|
||||||
|
|
||||||
keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
azureClient.VaultClient = common.NewVaultClient(*keyVaultURL)
|
|
||||||
azureClient.VaultClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalTokenVault)
|
|
||||||
azureClient.VaultClient.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.VaultClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VaultClient.UserAgent)
|
|
||||||
azureClient.VaultClient.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
// This client is different than the above because it manages the vault
|
|
||||||
// itself rather than the contents of the vault.
|
|
||||||
azureClient.VaultClientDelete = keyvault.NewVaultsClient(subscriptionID)
|
|
||||||
azureClient.VaultClientDelete.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
|
||||||
azureClient.VaultClientDelete.RequestInspector = withInspection(maxlen)
|
|
||||||
azureClient.VaultClientDelete.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
|
||||||
azureClient.VaultClientDelete.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.VaultClientDelete.UserAgent)
|
|
||||||
azureClient.VaultClientDelete.Client.PollingDuration = pollingDuration
|
|
||||||
|
|
||||||
// If this is a managed disk build, this should be ignored.
|
|
||||||
if resourceGroupName != "" && storageAccountName != "" {
|
|
||||||
accountKeys, err := azureClient.AccountsClient.ListKeys(context.TODO(), resourceGroupName, storageAccountName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
storageClient, err := storage.NewClient(
|
|
||||||
storageAccountName,
|
|
||||||
*(*accountKeys.Keys)[0].Value,
|
|
||||||
cloud.StorageEndpointSuffix,
|
|
||||||
storage.DefaultAPIVersion,
|
|
||||||
true /*useHttps*/)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
azureClient.BlobStorageClient = storageClient.GetBlobService()
|
|
||||||
}
|
|
||||||
|
|
||||||
return azureClient, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getInspectorMaxLength() int64 {
|
|
||||||
value, ok := os.LookupEnv(EnvPackerLogAzureMaxLen)
|
|
||||||
if !ok {
|
|
||||||
return math.MaxInt64
|
|
||||||
}
|
|
||||||
|
|
||||||
i, err := strconv.ParseInt(value, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if i < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return i
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type azureErrorDetails struct {
|
|
||||||
Code string `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Details []azureErrorDetails `json:"details"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type azureErrorResponse struct {
|
|
||||||
ErrorDetails azureErrorDetails `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAzureErrorResponse(s string) *azureErrorResponse {
|
|
||||||
var errorResponse azureErrorResponse
|
|
||||||
err := json.Unmarshal([]byte(s), &errorResponse)
|
|
||||||
if err == nil {
|
|
||||||
return &errorResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *azureErrorDetails) isEmpty() bool {
|
|
||||||
return e.Code == ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *azureErrorResponse) isEmpty() bool {
|
|
||||||
return e.ErrorDetails.isEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *azureErrorResponse) Error() string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
//buf.WriteString("-=-=- ERROR -=-=-")
|
|
||||||
formatAzureErrorResponse(e.ErrorDetails, &buf, "")
|
|
||||||
//buf.WriteString("-=-=- ERROR -=-=-")
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// format a Azure Error Response by recursing through the JSON structure.
|
|
||||||
//
|
|
||||||
// Errors may contain nested errors, which are JSON documents that have been
|
|
||||||
// serialized and escaped. Keep following this nesting all the way down...
|
|
||||||
func formatAzureErrorResponse(error azureErrorDetails, buf *bytes.Buffer, indent string) {
|
|
||||||
if error.isEmpty() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteString(fmt.Sprintf("ERROR: %s-> %s : %s\n", indent, error.Code, error.Message))
|
|
||||||
for _, x := range error.Details {
|
|
||||||
newIndent := fmt.Sprintf("%s ", indent)
|
|
||||||
|
|
||||||
var aer azureErrorResponse
|
|
||||||
err := json.Unmarshal([]byte(x.Message), &aer)
|
|
||||||
if err == nil {
|
|
||||||
buf.WriteString(fmt.Sprintf("ERROR: %s-> %s\n", newIndent, x.Code))
|
|
||||||
formatAzureErrorResponse(aer.ErrorDetails, buf, newIndent)
|
|
||||||
} else {
|
|
||||||
buf.WriteString(fmt.Sprintf("ERROR: %s-> %s : %s\n", newIndent, x.Code, x.Message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
ERROR: -> DeploymentFailed : At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.
|
|
||||||
ERROR: -> BadRequest
|
|
||||||
ERROR: -> InvalidRequestFormat : Cannot parse the request.
|
|
||||||
ERROR: -> InvalidJson : Error converting value "playground" to type 'Microsoft.WindowsAzure.Networking.Nrp.Frontend.Contract.Csm.Public.IpAllocationMethod'. Path 'properties.publicIPAllocationMethod', line 1, position 130.
|
|
|
@ -1 +0,0 @@
|
||||||
ERROR: -> ResourceNotFound : The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found.
|
|
|
@ -1,77 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
approvaltests "github.com/approvals/go-approval-tests"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
const AzureErrorSimple = `{"error":{"code":"ResourceNotFound","message":"The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found."}}`
|
|
||||||
const AzureErrorNested = `{"status":"Failed","error":{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.","details":[{"code":"BadRequest","message":"{\r\n \"error\": {\r\n \"code\": \"InvalidRequestFormat\",\r\n \"message\": \"Cannot parse the request.\",\r\n \"details\": [\r\n {\r\n \"code\": \"InvalidJson\",\r\n \"message\": \"Error converting value \\\"playground\\\" to type 'Microsoft.WindowsAzure.Networking.Nrp.Frontend.Contract.Csm.Public.IpAllocationMethod'. Path 'properties.publicIPAllocationMethod', line 1, position 130.\"\r\n }\r\n ]\r\n }\r\n}"}]}}`
|
|
||||||
|
|
||||||
func TestAzureErrorSimpleShouldUnmarshal(t *testing.T) {
|
|
||||||
var azureErrorResponse azureErrorResponse
|
|
||||||
err := json.Unmarshal([]byte(AzureErrorSimple), &azureErrorResponse)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if azureErrorResponse.ErrorDetails.Code != "ResourceNotFound" {
|
|
||||||
t.Errorf("Error.Code")
|
|
||||||
}
|
|
||||||
if azureErrorResponse.ErrorDetails.Message != "The Resource 'Microsoft.Compute/images/PackerUbuntuImage' under resource group 'packer-test00' was not found." {
|
|
||||||
t.Errorf("Error.Message")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAzureErrorNestedShouldUnmarshal(t *testing.T) {
|
|
||||||
var azureError azureErrorResponse
|
|
||||||
err := json.Unmarshal([]byte(AzureErrorNested), &azureError)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if azureError.ErrorDetails.Code != "DeploymentFailed" {
|
|
||||||
t.Errorf("Error.Code")
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(azureError.ErrorDetails.Message, "At least one resource deployment operation failed") {
|
|
||||||
t.Errorf("Error.Message")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAzureErrorEmptyShouldFormat(t *testing.T) {
|
|
||||||
var aer azureErrorResponse
|
|
||||||
s := aer.Error()
|
|
||||||
|
|
||||||
if s != "" {
|
|
||||||
t.Fatalf("Expected \"\", but got %s", aer.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAzureErrorSimpleShouldFormat(t *testing.T) {
|
|
||||||
var azureErrorResponse azureErrorResponse
|
|
||||||
err := json.Unmarshal([]byte(AzureErrorSimple), &azureErrorResponse)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = approvaltests.VerifyString(t, azureErrorResponse.Error())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAzureErrorNestedShouldFormat(t *testing.T) {
|
|
||||||
var azureErrorResponse azureErrorResponse
|
|
||||||
err := json.Unmarshal([]byte(AzureErrorNested), &azureErrorResponse)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = approvaltests.VerifyString(t, azureErrorResponse.Error())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,496 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
armstorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage"
|
|
||||||
"github.com/Azure/azure-sdk-for-go/storage"
|
|
||||||
"github.com/Azure/go-autorest/autorest/adal"
|
|
||||||
"github.com/dgrijalva/jwt-go"
|
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
packerAzureCommon "github.com/hashicorp/packer/builder/azure/common"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/lin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Builder struct {
|
|
||||||
config Config
|
|
||||||
stateBag multistep.StateBag
|
|
||||||
runner multistep.Runner
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
DefaultSasBlobContainer = "system/Microsoft.Compute"
|
|
||||||
DefaultSecretName = "packerKeyVaultSecret"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
|
|
||||||
|
|
||||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
|
||||||
warnings, errs := b.config.Prepare(raws...)
|
|
||||||
if errs != nil {
|
|
||||||
return nil, warnings, errs
|
|
||||||
}
|
|
||||||
|
|
||||||
b.stateBag = new(multistep.BasicStateBag)
|
|
||||||
b.configureStateBag(b.stateBag)
|
|
||||||
b.setTemplateParameters(b.stateBag)
|
|
||||||
b.setImageParameters(b.stateBag)
|
|
||||||
|
|
||||||
return nil, warnings, errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
|
|
||||||
|
|
||||||
ui.Say("Running builder ...")
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// FillParameters function captures authType and sets defaults.
|
|
||||||
err := b.config.ClientConfig.FillParameters()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//When running Packer on an Azure instance using Managed Identity, FillParameters will update SubscriptionID from the instance
|
|
||||||
// so lets make sure to update our state bag with the valid subscriptionID.
|
|
||||||
if b.config.isManagedImage() && b.config.SharedGalleryDestination.SigDestinationGalleryName != "" {
|
|
||||||
b.stateBag.Put(constants.ArmManagedImageSubscription, b.config.ClientConfig.SubscriptionID)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print(":: Configuration")
|
|
||||||
packerAzureCommon.DumpConfig(&b.config, func(s string) { log.Print(s) })
|
|
||||||
|
|
||||||
b.stateBag.Put("hook", hook)
|
|
||||||
b.stateBag.Put(constants.Ui, ui)
|
|
||||||
|
|
||||||
spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Message("Creating Azure Resource Manager (ARM) client ...")
|
|
||||||
azureClient, err := NewAzureClient(
|
|
||||||
b.config.ClientConfig.SubscriptionID,
|
|
||||||
b.config.SharedGalleryDestination.SigDestinationSubscription,
|
|
||||||
b.config.ResourceGroupName,
|
|
||||||
b.config.StorageAccount,
|
|
||||||
b.config.ClientConfig.CloudEnvironment(),
|
|
||||||
b.config.SharedGalleryTimeout,
|
|
||||||
b.config.PollingDurationTimeout,
|
|
||||||
spnCloud,
|
|
||||||
spnKeyVault)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
resolver := newResourceResolver(azureClient)
|
|
||||||
if err := resolver.Resolve(&b.config); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if b.config.ClientConfig.ObjectID == "" {
|
|
||||||
b.config.ClientConfig.ObjectID = getObjectIdFromToken(ui, spnCloud)
|
|
||||||
} else {
|
|
||||||
ui.Message("You have provided Object_ID which is no longer needed, azure packer builder determines this dynamically from the authentication token")
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.ClientConfig.ObjectID == "" && b.config.OSType != constants.Target_Linux {
|
|
||||||
return nil, fmt.Errorf("could not determine the ObjectID for the user, which is required for Windows builds")
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.isManagedImage() {
|
|
||||||
_, err := azureClient.GroupsClient.Get(ctx, b.config.ManagedImageResourceGroupName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Cannot locate the managed image resource group %s.", b.config.ManagedImageResourceGroupName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a managed image already exists it cannot be overwritten.
|
|
||||||
_, err = azureClient.ImagesClient.Get(ctx, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName, "")
|
|
||||||
if err == nil {
|
|
||||||
if b.config.PackerForce {
|
|
||||||
ui.Say(fmt.Sprintf("the managed image named %s already exists, but deleting it due to -force flag", b.config.ManagedImageName))
|
|
||||||
f, err := azureClient.ImagesClient.Delete(ctx, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName)
|
|
||||||
if err == nil {
|
|
||||||
err = f.WaitForCompletionRef(ctx, azureClient.ImagesClient.Client)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to delete the managed image named %s : %s", b.config.ManagedImageName, azureClient.LastError.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("the managed image named %s already exists in the resource group %s, use the -force option to automatically delete it.", b.config.ManagedImageName, b.config.ManagedImageResourceGroupName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// User is not using Managed Images to build, warning message here that this path is being deprecated
|
|
||||||
ui.Error("Warning: You are using Azure Packer Builder to create VHDs which is being deprecated, consider using Managed Images. Learn more https://www.packer.io/docs/builders/azure/arm#azure-arm-builder-specific-options")
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.BuildResourceGroupName != "" {
|
|
||||||
group, err := azureClient.GroupsClient.Get(ctx, b.config.BuildResourceGroupName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Cannot locate the existing build resource resource group %s.", b.config.BuildResourceGroupName)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.config.Location = *group.Location
|
|
||||||
}
|
|
||||||
|
|
||||||
b.config.validateLocationZoneResiliency(ui.Say)
|
|
||||||
|
|
||||||
if b.config.StorageAccount != "" {
|
|
||||||
account, err := b.getBlobAccount(ctx, azureClient, b.config.ResourceGroupName, b.config.StorageAccount)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b.config.storageAccountBlobEndpoint = *account.AccountProperties.PrimaryEndpoints.Blob
|
|
||||||
|
|
||||||
if !equalLocation(*account.Location, b.config.Location) {
|
|
||||||
return nil, fmt.Errorf("The storage account is located in %s, but the build will take place in %s. The locations must be identical", *account.Location, b.config.Location)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
endpointConnectType := PublicEndpoint
|
|
||||||
if b.isPublicPrivateNetworkCommunication() && b.isPrivateNetworkCommunication() {
|
|
||||||
endpointConnectType = PublicEndpointInPrivateNetwork
|
|
||||||
} else if b.isPrivateNetworkCommunication() {
|
|
||||||
endpointConnectType = PrivateEndpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
b.setRuntimeParameters(b.stateBag)
|
|
||||||
b.setTemplateParameters(b.stateBag)
|
|
||||||
b.setImageParameters(b.stateBag)
|
|
||||||
|
|
||||||
deploymentName := b.stateBag.Get(constants.ArmDeploymentName).(string)
|
|
||||||
|
|
||||||
// For Managed Images, validate that Shared Gallery Image exists before publishing to SIG
|
|
||||||
if b.config.isManagedImage() && b.config.SharedGalleryDestination.SigDestinationGalleryName != "" {
|
|
||||||
_, err = azureClient.GalleryImagesClient.Get(ctx, b.config.SharedGalleryDestination.SigDestinationResourceGroup, b.config.SharedGalleryDestination.SigDestinationGalleryName, b.config.SharedGalleryDestination.SigDestinationImageName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("the Shared Gallery Image to which to publish the managed image version to does not exist in the resource group %s", b.config.SharedGalleryDestination.SigDestinationResourceGroup)
|
|
||||||
}
|
|
||||||
// SIG requires that replication regions include the region in which the Managed Image resides
|
|
||||||
managedImageLocation := normalizeAzureRegion(b.stateBag.Get(constants.ArmLocation).(string))
|
|
||||||
foundMandatoryReplicationRegion := false
|
|
||||||
var normalizedReplicationRegions []string
|
|
||||||
for _, region := range b.config.SharedGalleryDestination.SigDestinationReplicationRegions {
|
|
||||||
// change region to lower-case and strip spaces
|
|
||||||
normalizedRegion := normalizeAzureRegion(region)
|
|
||||||
normalizedReplicationRegions = append(normalizedReplicationRegions, normalizedRegion)
|
|
||||||
if strings.EqualFold(normalizedRegion, managedImageLocation) {
|
|
||||||
foundMandatoryReplicationRegion = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if foundMandatoryReplicationRegion == false {
|
|
||||||
b.config.SharedGalleryDestination.SigDestinationReplicationRegions = append(normalizedReplicationRegions, managedImageLocation)
|
|
||||||
}
|
|
||||||
b.stateBag.Put(constants.ArmManagedImageSharedGalleryReplicationRegions, b.config.SharedGalleryDestination.SigDestinationReplicationRegions)
|
|
||||||
}
|
|
||||||
|
|
||||||
var steps []multistep.Step
|
|
||||||
if b.config.OSType == constants.Target_Linux {
|
|
||||||
steps = []multistep.Step{
|
|
||||||
NewStepCreateResourceGroup(azureClient, ui),
|
|
||||||
NewStepValidateTemplate(azureClient, ui, &b.config, GetVirtualMachineDeployment),
|
|
||||||
NewStepDeployTemplate(azureClient, ui, &b.config, deploymentName, GetVirtualMachineDeployment),
|
|
||||||
NewStepGetIPAddress(azureClient, ui, endpointConnectType),
|
|
||||||
&communicator.StepConnectSSH{
|
|
||||||
Config: &b.config.Comm,
|
|
||||||
Host: lin.SSHHost,
|
|
||||||
SSHConfig: b.config.Comm.SSHConfigFunc(),
|
|
||||||
},
|
|
||||||
&commonsteps.StepProvision{},
|
|
||||||
&commonsteps.StepCleanupTempKeys{
|
|
||||||
Comm: &b.config.Comm,
|
|
||||||
},
|
|
||||||
NewStepGetOSDisk(azureClient, ui),
|
|
||||||
NewStepGetAdditionalDisks(azureClient, ui),
|
|
||||||
NewStepPowerOffCompute(azureClient, ui),
|
|
||||||
NewStepSnapshotOSDisk(azureClient, ui, &b.config),
|
|
||||||
NewStepSnapshotDataDisks(azureClient, ui, &b.config),
|
|
||||||
NewStepCaptureImage(azureClient, ui),
|
|
||||||
NewStepPublishToSharedImageGallery(azureClient, ui, &b.config),
|
|
||||||
}
|
|
||||||
} else if b.config.OSType == constants.Target_Windows {
|
|
||||||
steps = []multistep.Step{
|
|
||||||
NewStepCreateResourceGroup(azureClient, ui),
|
|
||||||
}
|
|
||||||
if b.config.BuildKeyVaultName == "" {
|
|
||||||
keyVaultDeploymentName := b.stateBag.Get(constants.ArmKeyVaultDeploymentName).(string)
|
|
||||||
steps = append(steps,
|
|
||||||
NewStepValidateTemplate(azureClient, ui, &b.config, GetKeyVaultDeployment),
|
|
||||||
NewStepDeployTemplate(azureClient, ui, &b.config, keyVaultDeploymentName, GetKeyVaultDeployment),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
steps = append(steps, NewStepCertificateInKeyVault(&azureClient.VaultClient, ui, &b.config))
|
|
||||||
}
|
|
||||||
steps = append(steps,
|
|
||||||
NewStepGetCertificate(azureClient, ui),
|
|
||||||
NewStepSetCertificate(&b.config, ui),
|
|
||||||
NewStepValidateTemplate(azureClient, ui, &b.config, GetVirtualMachineDeployment),
|
|
||||||
NewStepDeployTemplate(azureClient, ui, &b.config, deploymentName, GetVirtualMachineDeployment),
|
|
||||||
NewStepGetIPAddress(azureClient, ui, endpointConnectType),
|
|
||||||
&communicator.StepConnectWinRM{
|
|
||||||
Config: &b.config.Comm,
|
|
||||||
Host: func(stateBag multistep.StateBag) (string, error) {
|
|
||||||
return stateBag.Get(constants.SSHHost).(string), nil
|
|
||||||
},
|
|
||||||
WinRMConfig: func(multistep.StateBag) (*communicator.WinRMConfig, error) {
|
|
||||||
return &communicator.WinRMConfig{
|
|
||||||
Username: b.config.UserName,
|
|
||||||
Password: b.config.Password,
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&commonsteps.StepProvision{},
|
|
||||||
NewStepGetOSDisk(azureClient, ui),
|
|
||||||
NewStepGetAdditionalDisks(azureClient, ui),
|
|
||||||
NewStepPowerOffCompute(azureClient, ui),
|
|
||||||
NewStepSnapshotOSDisk(azureClient, ui, &b.config),
|
|
||||||
NewStepSnapshotDataDisks(azureClient, ui, &b.config),
|
|
||||||
NewStepCaptureImage(azureClient, ui),
|
|
||||||
NewStepPublishToSharedImageGallery(azureClient, ui, &b.config),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.PackerDebug {
|
|
||||||
ui.Message(fmt.Sprintf("temp admin user: '%s'", b.config.UserName))
|
|
||||||
ui.Message(fmt.Sprintf("temp admin password: '%s'", b.config.Password))
|
|
||||||
|
|
||||||
if len(b.config.Comm.SSHPrivateKey) != 0 {
|
|
||||||
debugKeyPath := fmt.Sprintf("%s-%s.pem", b.config.PackerBuildName, b.config.tmpComputeName)
|
|
||||||
ui.Message(fmt.Sprintf("temp ssh key: %s", debugKeyPath))
|
|
||||||
|
|
||||||
b.writeSSHPrivateKey(ui, debugKeyPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
|
|
||||||
b.runner.Run(ctx, b.stateBag)
|
|
||||||
|
|
||||||
// Report any errors.
|
|
||||||
if rawErr, ok := b.stateBag.GetOk(constants.Error); ok {
|
|
||||||
return nil, rawErr.(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we were interrupted or cancelled, then just exit.
|
|
||||||
if _, ok := b.stateBag.GetOk(multistep.StateCancelled); ok {
|
|
||||||
return nil, errors.New("Build was cancelled.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := b.stateBag.GetOk(multistep.StateHalted); ok {
|
|
||||||
return nil, errors.New("Build was halted.")
|
|
||||||
}
|
|
||||||
|
|
||||||
getSasUrlFunc := func(name string) string {
|
|
||||||
blob := azureClient.BlobStorageClient.GetContainerReference(DefaultSasBlobContainer).GetBlobReference(name)
|
|
||||||
options := storage.BlobSASOptions{}
|
|
||||||
options.BlobServiceSASPermissions.Read = true
|
|
||||||
options.Expiry = time.Now().AddDate(0, 1, 0).UTC() // one month
|
|
||||||
sasUrl, _ := blob.GetSASURI(options)
|
|
||||||
return sasUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
generatedData := map[string]interface{}{"generated_data": b.stateBag.Get("generated_data")}
|
|
||||||
if b.config.isManagedImage() {
|
|
||||||
managedImageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s",
|
|
||||||
b.config.ClientConfig.SubscriptionID, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName)
|
|
||||||
if b.config.SharedGalleryDestination.SigDestinationGalleryName != "" {
|
|
||||||
return NewManagedImageArtifactWithSIGAsDestination(b.config.OSType,
|
|
||||||
b.config.ManagedImageResourceGroupName,
|
|
||||||
b.config.ManagedImageName,
|
|
||||||
b.config.Location,
|
|
||||||
managedImageID,
|
|
||||||
b.config.ManagedImageOSDiskSnapshotName,
|
|
||||||
b.config.ManagedImageDataDiskSnapshotPrefix,
|
|
||||||
b.stateBag.Get(constants.ArmManagedImageSharedGalleryId).(string),
|
|
||||||
generatedData)
|
|
||||||
} else if template, ok := b.stateBag.GetOk(constants.ArmCaptureTemplate); ok {
|
|
||||||
return NewManagedImageArtifact(b.config.OSType,
|
|
||||||
b.config.ManagedImageResourceGroupName,
|
|
||||||
b.config.ManagedImageName,
|
|
||||||
b.config.Location,
|
|
||||||
managedImageID,
|
|
||||||
b.config.ManagedImageOSDiskSnapshotName,
|
|
||||||
b.config.ManagedImageDataDiskSnapshotPrefix,
|
|
||||||
generatedData,
|
|
||||||
b.stateBag.Get(constants.ArmKeepOSDisk).(bool),
|
|
||||||
template.(*CaptureTemplate),
|
|
||||||
getSasUrlFunc)
|
|
||||||
}
|
|
||||||
return NewManagedImageArtifact(b.config.OSType,
|
|
||||||
b.config.ManagedImageResourceGroupName,
|
|
||||||
b.config.ManagedImageName,
|
|
||||||
b.config.Location,
|
|
||||||
managedImageID,
|
|
||||||
b.config.ManagedImageOSDiskSnapshotName,
|
|
||||||
b.config.ManagedImageDataDiskSnapshotPrefix,
|
|
||||||
generatedData,
|
|
||||||
b.stateBag.Get(constants.ArmKeepOSDisk).(bool),
|
|
||||||
nil,
|
|
||||||
getSasUrlFunc)
|
|
||||||
} else if template, ok := b.stateBag.GetOk(constants.ArmCaptureTemplate); ok {
|
|
||||||
return NewArtifact(
|
|
||||||
template.(*CaptureTemplate),
|
|
||||||
getSasUrlFunc,
|
|
||||||
b.config.OSType,
|
|
||||||
generatedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Artifact{
|
|
||||||
StateData: generatedData,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) writeSSHPrivateKey(ui packersdk.Ui, debugKeyPath string) {
|
|
||||||
f, err := os.Create(debugKeyPath)
|
|
||||||
if err != nil {
|
|
||||||
ui.Say(fmt.Sprintf("Error saving debug key: %s", err))
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
// Write the key out
|
|
||||||
if _, err := f.Write(b.config.Comm.SSHPrivateKey); err != nil {
|
|
||||||
ui.Say(fmt.Sprintf("Error saving debug key: %s", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chmod it so that it is SSH ready
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
if err := f.Chmod(0600); err != nil {
|
|
||||||
ui.Say(fmt.Sprintf("Error setting permissions of debug key: %s", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) isPublicPrivateNetworkCommunication() bool {
|
|
||||||
return DefaultPrivateVirtualNetworkWithPublicIp != b.config.PrivateVirtualNetworkWithPublicIp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) isPrivateNetworkCommunication() bool {
|
|
||||||
return b.config.VirtualNetworkName != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func equalLocation(location1, location2 string) bool {
|
|
||||||
return strings.EqualFold(canonicalizeLocation(location1), canonicalizeLocation(location2))
|
|
||||||
}
|
|
||||||
|
|
||||||
func canonicalizeLocation(location string) string {
|
|
||||||
return strings.Replace(location, " ", "", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) getBlobAccount(ctx context.Context, client *AzureClient, resourceGroupName string, storageAccountName string) (*armstorage.Account, error) {
|
|
||||||
account, err := client.AccountsClient.GetProperties(ctx, resourceGroupName, storageAccountName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &account, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) configureStateBag(stateBag multistep.StateBag) {
|
|
||||||
stateBag.Put(constants.AuthorizedKey, b.config.sshAuthorizedKey)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmTags, packerAzureCommon.MapToAzureTags(b.config.AzureTags))
|
|
||||||
stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName)
|
|
||||||
stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName)
|
|
||||||
|
|
||||||
if b.config.OSType == constants.Target_Windows && b.config.BuildKeyVaultName == "" {
|
|
||||||
stateBag.Put(constants.ArmKeyVaultDeploymentName, fmt.Sprintf("kv%s", b.config.tmpDeploymentName))
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmKeyVaultName, b.config.tmpKeyVaultName)
|
|
||||||
stateBag.Put(constants.ArmIsExistingKeyVault, false)
|
|
||||||
if b.config.BuildKeyVaultName != "" {
|
|
||||||
stateBag.Put(constants.ArmKeyVaultName, b.config.BuildKeyVaultName)
|
|
||||||
b.config.tmpKeyVaultName = b.config.BuildKeyVaultName
|
|
||||||
stateBag.Put(constants.ArmIsExistingKeyVault, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmNicName, b.config.tmpNicName)
|
|
||||||
stateBag.Put(constants.ArmPublicIPAddressName, b.config.tmpPublicIPAddressName)
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, b.config.BuildResourceGroupName)
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
|
|
||||||
|
|
||||||
if b.config.tmpResourceGroupName != "" {
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, b.config.tmpResourceGroupName)
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, false)
|
|
||||||
|
|
||||||
if b.config.BuildResourceGroupName != "" {
|
|
||||||
stateBag.Put(constants.ArmDoubleResourceGroupNameSet, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmStorageAccountName, b.config.StorageAccount)
|
|
||||||
stateBag.Put(constants.ArmIsManagedImage, b.config.isManagedImage())
|
|
||||||
stateBag.Put(constants.ArmManagedImageResourceGroupName, b.config.ManagedImageResourceGroupName)
|
|
||||||
stateBag.Put(constants.ArmManagedImageName, b.config.ManagedImageName)
|
|
||||||
stateBag.Put(constants.ArmManagedImageOSDiskSnapshotName, b.config.ManagedImageOSDiskSnapshotName)
|
|
||||||
stateBag.Put(constants.ArmManagedImageDataDiskSnapshotPrefix, b.config.ManagedImageDataDiskSnapshotPrefix)
|
|
||||||
stateBag.Put(constants.ArmAsyncResourceGroupDelete, b.config.AsyncResourceGroupDelete)
|
|
||||||
stateBag.Put(constants.ArmKeepOSDisk, b.config.KeepOSDisk)
|
|
||||||
|
|
||||||
if b.config.isManagedImage() && b.config.SharedGalleryDestination.SigDestinationGalleryName != "" {
|
|
||||||
stateBag.Put(constants.ArmManagedImageSigPublishResourceGroup, b.config.SharedGalleryDestination.SigDestinationResourceGroup)
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryName, b.config.SharedGalleryDestination.SigDestinationGalleryName)
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryImageName, b.config.SharedGalleryDestination.SigDestinationImageName)
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersion, b.config.SharedGalleryDestination.SigDestinationImageVersion)
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionStorageAccountType, b.config.SharedGalleryDestination.SigDestinationStorageAccountType)
|
|
||||||
stateBag.Put(constants.ArmManagedImageSubscription, b.config.ClientConfig.SubscriptionID)
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionEndOfLifeDate, b.config.SharedGalleryImageVersionEndOfLifeDate)
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionReplicaCount, b.config.SharedGalleryImageVersionReplicaCount)
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionExcludeFromLatest, b.config.SharedGalleryImageVersionExcludeFromLatest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parameters that are only known at runtime after querying Azure.
|
|
||||||
func (b *Builder) setRuntimeParameters(stateBag multistep.StateBag) {
|
|
||||||
stateBag.Put(constants.ArmLocation, b.config.Location)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) setTemplateParameters(stateBag multistep.StateBag) {
|
|
||||||
stateBag.Put(constants.ArmVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) setImageParameters(stateBag multistep.StateBag) {
|
|
||||||
stateBag.Put(constants.ArmImageParameters, b.config.toImageParameters())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) getServicePrincipalTokens(say func(string)) (*adal.ServicePrincipalToken, *adal.ServicePrincipalToken, error) {
|
|
||||||
return b.config.ClientConfig.GetServicePrincipalTokens(say)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getObjectIdFromToken(ui packersdk.Ui, token *adal.ServicePrincipalToken) string {
|
|
||||||
claims := jwt.MapClaims{}
|
|
||||||
var p jwt.Parser
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
_, _, err = p.ParseUnverified(token.OAuthToken(), claims)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf("Failed to parse the token,Error: %s", err.Error()))
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
oid, _ := claims["oid"].(string)
|
|
||||||
return oid
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalizeAzureRegion(name string) string {
|
|
||||||
return strings.ToLower(strings.Replace(name, " ", "", -1))
|
|
||||||
}
|
|
|
@ -1,513 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
// these tests require the following variables to be set,
|
|
||||||
// although some test will only use a subset:
|
|
||||||
//
|
|
||||||
// * ARM_CLIENT_ID
|
|
||||||
// * ARM_CLIENT_SECRET
|
|
||||||
// * ARM_SUBSCRIPTION_ID
|
|
||||||
// * ARM_STORAGE_ACCOUNT
|
|
||||||
//
|
|
||||||
// The subscription in question should have a resource group
|
|
||||||
// called "packer-acceptance-test" in "South Central US" region. The
|
|
||||||
// storage account referred to in the above variable should
|
|
||||||
// be inside this resource group and in "South Central US" as well.
|
|
||||||
//
|
|
||||||
// In addition, the PACKER_ACC variable should also be set to
|
|
||||||
// a non-empty value to enable Packer acceptance tests and the
|
|
||||||
// options "-v -timeout 90m" should be provided to the test
|
|
||||||
// command, e.g.:
|
|
||||||
// go test -v -timeout 90m -run TestBuilderAcc_.*
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Azure/go-autorest/autorest"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure/auth"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
builderT "github.com/hashicorp/packer/acctest"
|
|
||||||
)
|
|
||||||
|
|
||||||
const DeviceLoginAcceptanceTest = "DEVICELOGIN_TEST"
|
|
||||||
|
|
||||||
func TestBuilderAcc_ManagedDisk_Windows(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccManagedDiskWindows,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_ManagedDisk_Windows_Build_Resource_Group(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccManagedDiskWindowsBuildResourceGroup,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_ManagedDisk_Windows_Build_Resource_Group_Additional_Disk(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccManagedDiskWindowsBuildResourceGroupAdditionalDisk,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_ManagedDisk_Windows_DeviceLogin(t *testing.T) {
|
|
||||||
if os.Getenv(DeviceLoginAcceptanceTest) == "" {
|
|
||||||
t.Skip(fmt.Sprintf(
|
|
||||||
"Device Login Acceptance tests skipped unless env '%s' set, as its requires manual step during execution",
|
|
||||||
DeviceLoginAcceptanceTest))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccManagedDiskWindowsDeviceLogin,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_ManagedDisk_Linux(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccManagedDiskLinux,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_ManagedDisk_Linux_DeviceLogin(t *testing.T) {
|
|
||||||
if os.Getenv(DeviceLoginAcceptanceTest) == "" {
|
|
||||||
t.Skip(fmt.Sprintf(
|
|
||||||
"Device Login Acceptance tests skipped unless env '%s' set, as its requires manual step during execution",
|
|
||||||
DeviceLoginAcceptanceTest))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccManagedDiskLinuxDeviceLogin,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_ManagedDisk_Linux_AzureCLI(t *testing.T) {
|
|
||||||
if os.Getenv("AZURE_CLI_AUTH") == "" {
|
|
||||||
t.Skip("Azure CLI Acceptance tests skipped unless env 'AZURE_CLI_AUTH' is set, and an active `az login` session has been established")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var b Builder
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAuthPreCheck(t) },
|
|
||||||
Builder: &b,
|
|
||||||
Template: testBuilderAccManagedDiskLinuxAzureCLI,
|
|
||||||
Check: func([]packersdk.Artifact) error {
|
|
||||||
checkTemporaryGroupDeleted(t, &b)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_Blob_Windows(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccBlobWindows,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_Blob_Linux(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAuthPreCheck(t) },
|
|
||||||
Builder: &b,
|
|
||||||
Template: testBuilderAccBlobLinux,
|
|
||||||
Check: func([]packersdk.Artifact) error {
|
|
||||||
checkUnmanagedVHDDeleted(t, &b)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAccPreCheck(*testing.T) {}
|
|
||||||
|
|
||||||
func testAuthPreCheck(t *testing.T) {
|
|
||||||
_, err := auth.NewAuthorizerFromEnvironment()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to auth to azure: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkTemporaryGroupDeleted(t *testing.T, b *Builder) {
|
|
||||||
ui := testUi()
|
|
||||||
|
|
||||||
spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed getting azure tokens: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Message("Creating test Azure Resource Manager (ARM) client ...")
|
|
||||||
azureClient, err := NewAzureClient(
|
|
||||||
b.config.ClientConfig.SubscriptionID,
|
|
||||||
b.config.SharedGalleryDestination.SigDestinationSubscription,
|
|
||||||
b.config.ResourceGroupName,
|
|
||||||
b.config.StorageAccount,
|
|
||||||
b.config.ClientConfig.CloudEnvironment(),
|
|
||||||
b.config.SharedGalleryTimeout,
|
|
||||||
b.config.PollingDurationTimeout,
|
|
||||||
spnCloud,
|
|
||||||
spnKeyVault)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create azure client: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate resource group has been deleted
|
|
||||||
_, err = azureClient.GroupsClient.Get(context.Background(), b.config.tmpResourceGroupName)
|
|
||||||
if err == nil || !resourceNotFound(err) {
|
|
||||||
t.Fatalf("failed validating resource group deletion: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkUnmanagedVHDDeleted(t *testing.T, b *Builder) {
|
|
||||||
ui := testUi()
|
|
||||||
|
|
||||||
spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed getting azure tokens: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
azureClient, err := NewAzureClient(
|
|
||||||
b.config.ClientConfig.SubscriptionID,
|
|
||||||
b.config.SharedGalleryDestination.SigDestinationSubscription,
|
|
||||||
b.config.ResourceGroupName,
|
|
||||||
b.config.StorageAccount,
|
|
||||||
b.config.ClientConfig.CloudEnvironment(),
|
|
||||||
b.config.SharedGalleryTimeout,
|
|
||||||
b.config.PollingDurationTimeout,
|
|
||||||
spnCloud,
|
|
||||||
spnKeyVault)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create azure client: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate temporary os blob was deleted
|
|
||||||
blob := azureClient.BlobStorageClient.GetContainerReference("images").GetBlobReference(b.config.tmpOSDiskName)
|
|
||||||
_, err = blob.BreakLease(nil)
|
|
||||||
if err != nil && !strings.Contains(err.Error(), "BlobNotFound") {
|
|
||||||
t.Fatalf("failed validating deletion of unmanaged vhd: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate resource group has been deleted
|
|
||||||
_, err = azureClient.GroupsClient.Get(context.Background(), b.config.tmpResourceGroupName)
|
|
||||||
if err == nil || !resourceNotFound(err) {
|
|
||||||
t.Fatalf("failed validating resource group deletion: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceNotFound(err error) bool {
|
|
||||||
derr := autorest.DetailedError{}
|
|
||||||
return errors.As(err, &derr) && derr.StatusCode == 404
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUi() *packersdk.BasicUi {
|
|
||||||
return &packersdk.BasicUi{
|
|
||||||
Reader: new(bytes.Buffer),
|
|
||||||
Writer: new(bytes.Buffer),
|
|
||||||
ErrorWriter: new(bytes.Buffer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const testBuilderAccManagedDiskWindows = `
|
|
||||||
{
|
|
||||||
"variables": {
|
|
||||||
"client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}",
|
|
||||||
"client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}",
|
|
||||||
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}"
|
|
||||||
},
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
|
|
||||||
"client_id": "{{user ` + "`client_id`" + `}}",
|
|
||||||
"client_secret": "{{user ` + "`client_secret`" + `}}",
|
|
||||||
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
|
||||||
|
|
||||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
|
||||||
"managed_image_name": "testBuilderAccManagedDiskWindows-{{timestamp}}",
|
|
||||||
|
|
||||||
"os_type": "Windows",
|
|
||||||
"image_publisher": "MicrosoftWindowsServer",
|
|
||||||
"image_offer": "WindowsServer",
|
|
||||||
"image_sku": "2012-R2-Datacenter",
|
|
||||||
|
|
||||||
"communicator": "winrm",
|
|
||||||
"winrm_use_ssl": "true",
|
|
||||||
"winrm_insecure": "true",
|
|
||||||
"winrm_timeout": "3m",
|
|
||||||
"winrm_username": "packer",
|
|
||||||
"async_resourcegroup_delete": "true",
|
|
||||||
|
|
||||||
"location": "South Central US",
|
|
||||||
"vm_size": "Standard_DS2_v2"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccManagedDiskWindowsBuildResourceGroup = `
|
|
||||||
{
|
|
||||||
"variables": {
|
|
||||||
"client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}",
|
|
||||||
"client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}",
|
|
||||||
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}"
|
|
||||||
},
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
|
|
||||||
"client_id": "{{user ` + "`client_id`" + `}}",
|
|
||||||
"client_secret": "{{user ` + "`client_secret`" + `}}",
|
|
||||||
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
|
||||||
|
|
||||||
"build_resource_group_name" : "packer-acceptance-test",
|
|
||||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
|
||||||
"managed_image_name": "testBuilderAccManagedDiskWindowsBuildResourceGroup-{{timestamp}}",
|
|
||||||
|
|
||||||
"os_type": "Windows",
|
|
||||||
"image_publisher": "MicrosoftWindowsServer",
|
|
||||||
"image_offer": "WindowsServer",
|
|
||||||
"image_sku": "2012-R2-Datacenter",
|
|
||||||
|
|
||||||
"communicator": "winrm",
|
|
||||||
"winrm_use_ssl": "true",
|
|
||||||
"winrm_insecure": "true",
|
|
||||||
"winrm_timeout": "3m",
|
|
||||||
"winrm_username": "packer",
|
|
||||||
"async_resourcegroup_delete": "true",
|
|
||||||
|
|
||||||
"vm_size": "Standard_DS2_v2"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccManagedDiskWindowsBuildResourceGroupAdditionalDisk = `
|
|
||||||
{
|
|
||||||
"variables": {
|
|
||||||
"client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}",
|
|
||||||
"client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}",
|
|
||||||
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}"
|
|
||||||
},
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
|
|
||||||
"client_id": "{{user ` + "`client_id`" + `}}",
|
|
||||||
"client_secret": "{{user ` + "`client_secret`" + `}}",
|
|
||||||
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
|
||||||
|
|
||||||
"build_resource_group_name" : "packer-acceptance-test",
|
|
||||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
|
||||||
"managed_image_name": "testBuilderAccManagedDiskWindowsBuildResourceGroupAdditionDisk-{{timestamp}}",
|
|
||||||
|
|
||||||
"os_type": "Windows",
|
|
||||||
"image_publisher": "MicrosoftWindowsServer",
|
|
||||||
"image_offer": "WindowsServer",
|
|
||||||
"image_sku": "2012-R2-Datacenter",
|
|
||||||
|
|
||||||
"communicator": "winrm",
|
|
||||||
"winrm_use_ssl": "true",
|
|
||||||
"winrm_insecure": "true",
|
|
||||||
"winrm_timeout": "3m",
|
|
||||||
"winrm_username": "packer",
|
|
||||||
"async_resourcegroup_delete": "true",
|
|
||||||
|
|
||||||
"vm_size": "Standard_DS2_v2",
|
|
||||||
"disk_additional_size": [10,15]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccManagedDiskWindowsDeviceLogin = `
|
|
||||||
{
|
|
||||||
"variables": {
|
|
||||||
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}"
|
|
||||||
},
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
|
|
||||||
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
|
||||||
|
|
||||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
|
||||||
"managed_image_name": "testBuilderAccManagedDiskWindowsDeviceLogin-{{timestamp}}",
|
|
||||||
|
|
||||||
"os_type": "Windows",
|
|
||||||
"image_publisher": "MicrosoftWindowsServer",
|
|
||||||
"image_offer": "WindowsServer",
|
|
||||||
"image_sku": "2012-R2-Datacenter",
|
|
||||||
|
|
||||||
"communicator": "winrm",
|
|
||||||
"winrm_use_ssl": "true",
|
|
||||||
"winrm_insecure": "true",
|
|
||||||
"winrm_timeout": "3m",
|
|
||||||
"winrm_username": "packer",
|
|
||||||
|
|
||||||
"location": "South Central US",
|
|
||||||
"vm_size": "Standard_DS2_v2"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccManagedDiskLinux = `
|
|
||||||
{
|
|
||||||
"variables": {
|
|
||||||
"client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}",
|
|
||||||
"client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}",
|
|
||||||
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}"
|
|
||||||
},
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
|
|
||||||
"client_id": "{{user ` + "`client_id`" + `}}",
|
|
||||||
"client_secret": "{{user ` + "`client_secret`" + `}}",
|
|
||||||
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
|
||||||
|
|
||||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
|
||||||
"managed_image_name": "testBuilderAccManagedDiskLinux-{{timestamp}}",
|
|
||||||
|
|
||||||
"os_type": "Linux",
|
|
||||||
"image_publisher": "Canonical",
|
|
||||||
"image_offer": "UbuntuServer",
|
|
||||||
"image_sku": "16.04-LTS",
|
|
||||||
|
|
||||||
"location": "South Central US",
|
|
||||||
"vm_size": "Standard_DS2_v2",
|
|
||||||
"azure_tags": {
|
|
||||||
"env": "testing",
|
|
||||||
"builder": "packer"
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccManagedDiskLinuxDeviceLogin = `
|
|
||||||
{
|
|
||||||
"variables": {
|
|
||||||
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}"
|
|
||||||
},
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
|
|
||||||
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
|
||||||
|
|
||||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
|
||||||
"managed_image_name": "testBuilderAccManagedDiskLinuxDeviceLogin-{{timestamp}}",
|
|
||||||
|
|
||||||
"os_type": "Linux",
|
|
||||||
"image_publisher": "Canonical",
|
|
||||||
"image_offer": "UbuntuServer",
|
|
||||||
"image_sku": "16.04-LTS",
|
|
||||||
"async_resourcegroup_delete": "true",
|
|
||||||
|
|
||||||
"location": "South Central US",
|
|
||||||
"vm_size": "Standard_DS2_v2"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccBlobWindows = `
|
|
||||||
{
|
|
||||||
"variables": {
|
|
||||||
"client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}",
|
|
||||||
"client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}",
|
|
||||||
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}",
|
|
||||||
"storage_account": "{{env ` + "`ARM_STORAGE_ACCOUNT`" + `}}"
|
|
||||||
},
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
|
|
||||||
"client_id": "{{user ` + "`client_id`" + `}}",
|
|
||||||
"client_secret": "{{user ` + "`client_secret`" + `}}",
|
|
||||||
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
|
||||||
|
|
||||||
"storage_account": "{{user ` + "`storage_account`" + `}}",
|
|
||||||
"resource_group_name": "packer-acceptance-test",
|
|
||||||
"capture_container_name": "test",
|
|
||||||
"capture_name_prefix": "testBuilderAccBlobWin",
|
|
||||||
|
|
||||||
"os_type": "Windows",
|
|
||||||
"image_publisher": "MicrosoftWindowsServer",
|
|
||||||
"image_offer": "WindowsServer",
|
|
||||||
"image_sku": "2012-R2-Datacenter",
|
|
||||||
|
|
||||||
"communicator": "winrm",
|
|
||||||
"winrm_use_ssl": "true",
|
|
||||||
"winrm_insecure": "true",
|
|
||||||
"winrm_timeout": "3m",
|
|
||||||
"winrm_username": "packer",
|
|
||||||
|
|
||||||
"location": "South Central US",
|
|
||||||
"vm_size": "Standard_DS2_v2"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccBlobLinux = `
|
|
||||||
{
|
|
||||||
"variables": {
|
|
||||||
"client_id": "{{env ` + "`ARM_CLIENT_ID`" + `}}",
|
|
||||||
"client_secret": "{{env ` + "`ARM_CLIENT_SECRET`" + `}}",
|
|
||||||
"subscription_id": "{{env ` + "`ARM_SUBSCRIPTION_ID`" + `}}",
|
|
||||||
"storage_account": "{{env ` + "`ARM_STORAGE_ACCOUNT`" + `}}"
|
|
||||||
},
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
|
|
||||||
"client_id": "{{user ` + "`client_id`" + `}}",
|
|
||||||
"client_secret": "{{user ` + "`client_secret`" + `}}",
|
|
||||||
"subscription_id": "{{user ` + "`subscription_id`" + `}}",
|
|
||||||
|
|
||||||
"storage_account": "{{user ` + "`storage_account`" + `}}",
|
|
||||||
"resource_group_name": "packer-acceptance-test",
|
|
||||||
"capture_container_name": "test",
|
|
||||||
"capture_name_prefix": "testBuilderAccBlobLinux",
|
|
||||||
|
|
||||||
"os_type": "Linux",
|
|
||||||
"image_publisher": "Canonical",
|
|
||||||
"image_offer": "UbuntuServer",
|
|
||||||
"image_sku": "16.04-LTS",
|
|
||||||
|
|
||||||
"location": "South Central US",
|
|
||||||
"vm_size": "Standard_DS2_v2"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccManagedDiskLinuxAzureCLI = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
|
|
||||||
"use_azure_cli_auth": true,
|
|
||||||
|
|
||||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
|
||||||
"managed_image_name": "testBuilderAccManagedDiskLinuxAzureCLI-{{timestamp}}",
|
|
||||||
"temp_resource_group_name": "packer-acceptance-test-managed-cli",
|
|
||||||
|
|
||||||
"os_type": "Linux",
|
|
||||||
"image_publisher": "Canonical",
|
|
||||||
"image_offer": "UbuntuServer",
|
|
||||||
"image_sku": "16.04-LTS",
|
|
||||||
|
|
||||||
"location": "South Central US",
|
|
||||||
"vm_size": "Standard_DS2_v2",
|
|
||||||
"azure_tags": {
|
|
||||||
"env": "testing",
|
|
||||||
"builder": "packer"
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,67 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) {
|
|
||||||
var testSubject Builder
|
|
||||||
_, _, err := testSubject.Prepare(getArmBuilderConfiguration(), getPackerConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to prepare: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedStateBagKeys = []string{
|
|
||||||
constants.AuthorizedKey,
|
|
||||||
|
|
||||||
constants.ArmTags,
|
|
||||||
constants.ArmComputeName,
|
|
||||||
constants.ArmDeploymentName,
|
|
||||||
constants.ArmNicName,
|
|
||||||
constants.ArmResourceGroupName,
|
|
||||||
constants.ArmStorageAccountName,
|
|
||||||
constants.ArmVirtualMachineCaptureParameters,
|
|
||||||
constants.ArmPublicIPAddressName,
|
|
||||||
constants.ArmAsyncResourceGroupDelete,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range expectedStateBagKeys {
|
|
||||||
if _, ok := testSubject.stateBag.GetOk(v); ok == false {
|
|
||||||
t.Errorf("Expected the builder's state bag to contain '%s', but it did not.", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStateBagShouldPoluateExpectedTags(t *testing.T) {
|
|
||||||
var testSubject Builder
|
|
||||||
|
|
||||||
expectedTags := map[string]string{
|
|
||||||
"env": "test",
|
|
||||||
"builder": "packer",
|
|
||||||
}
|
|
||||||
armConfig := getArmBuilderConfiguration()
|
|
||||||
armConfig["azure_tags"] = expectedTags
|
|
||||||
|
|
||||||
_, _, err := testSubject.Prepare(armConfig, getPackerConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to prepare: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tags, ok := testSubject.stateBag.Get(constants.ArmTags).(map[string]*string)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("Expected the builder's state bag to contain tags of type %T, but didn't.", testSubject.config.AzureTags)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(tags) != len(expectedTags) {
|
|
||||||
t.Errorf("expect tags from state to be the same length as tags from config")
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range tags {
|
|
||||||
if expectedTags[k] != *v {
|
|
||||||
t.Errorf("expect tag value of %s to be %s, but got %s", k, expectedTags[k], *v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
type CaptureTemplateParameter struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
DefaultValue string `json:"defaultValue,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureHardwareProfile struct {
|
|
||||||
VMSize string `json:"vmSize"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureUri struct {
|
|
||||||
Uri string `json:"uri"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureDisk struct {
|
|
||||||
OSType string `json:"osType"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Image CaptureUri `json:"image"`
|
|
||||||
Vhd CaptureUri `json:"vhd"`
|
|
||||||
CreateOption string `json:"createOption"`
|
|
||||||
Caching string `json:"caching"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureStorageProfile struct {
|
|
||||||
OSDisk CaptureDisk `json:"osDisk"`
|
|
||||||
DataDisks []CaptureDisk `json:"dataDisks"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureOSProfile struct {
|
|
||||||
ComputerName string `json:"computerName"`
|
|
||||||
AdminUsername string `json:"adminUsername"`
|
|
||||||
AdminPassword string `json:"adminPassword"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureNetworkInterface struct {
|
|
||||||
Id string `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureNetworkProfile struct {
|
|
||||||
NetworkInterfaces []CaptureNetworkInterface `json:"networkInterfaces"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureBootDiagnostics struct {
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureDiagnosticProfile struct {
|
|
||||||
BootDiagnostics CaptureBootDiagnostics `json:"bootDiagnostics"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureProperties struct {
|
|
||||||
HardwareProfile CaptureHardwareProfile `json:"hardwareProfile"`
|
|
||||||
StorageProfile CaptureStorageProfile `json:"storageProfile"`
|
|
||||||
OSProfile CaptureOSProfile `json:"osProfile"`
|
|
||||||
NetworkProfile CaptureNetworkProfile `json:"networkProfile"`
|
|
||||||
DiagnosticsProfile CaptureDiagnosticProfile `json:"diagnosticsProfile"`
|
|
||||||
ProvisioningState int `json:"provisioningState"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureResources struct {
|
|
||||||
ApiVersion string `json:"apiVersion"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Location string `json:"location"`
|
|
||||||
Properties CaptureProperties `json:"properties"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureTemplate struct {
|
|
||||||
Schema string `json:"$schema"`
|
|
||||||
ContentVersion string `json:"contentVersion"`
|
|
||||||
Parameters map[string]CaptureTemplateParameter `json:"parameters"`
|
|
||||||
Resources []CaptureResources `json:"resources"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureOperationProperties struct {
|
|
||||||
Output *CaptureTemplate `json:"output"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CaptureOperation struct {
|
|
||||||
OperationId string `json:"operationId"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
Properties *CaptureOperationProperties `json:"properties"`
|
|
||||||
}
|
|
|
@ -1,272 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var captureTemplate01 = `{
|
|
||||||
"operationId": "ac1c7c38-a591-41b3-89bd-ea39fceace1b",
|
|
||||||
"status": "Succeeded",
|
|
||||||
"startTime": "2016-04-04T21:07:25.2900874+00:00",
|
|
||||||
"endTime": "2016-04-04T21:07:26.4776321+00:00",
|
|
||||||
"properties": {
|
|
||||||
"output": {
|
|
||||||
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/VM_IP.json",
|
|
||||||
"contentVersion": "1.0.0.0",
|
|
||||||
"parameters": {
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string",
|
|
||||||
"defaultValue": "Standard_A2"
|
|
||||||
},
|
|
||||||
"adminUserName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"adminPassword": {
|
|
||||||
"type": "securestring"
|
|
||||||
},
|
|
||||||
"networkInterfaceId": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "2015-06-15",
|
|
||||||
"properties": {
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"osDisk": {
|
|
||||||
"osType": "Linux",
|
|
||||||
"name": "packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"image": {
|
|
||||||
"uri": "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd"
|
|
||||||
},
|
|
||||||
"vhd": {
|
|
||||||
"uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/osDisk.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd"
|
|
||||||
},
|
|
||||||
"caching": "ReadWrite"
|
|
||||||
},
|
|
||||||
"dataDisks": [
|
|
||||||
{
|
|
||||||
"lun": 0,
|
|
||||||
"name": "packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd",
|
|
||||||
"createOption": "Empty",
|
|
||||||
"image": {
|
|
||||||
"uri": "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd"
|
|
||||||
},
|
|
||||||
"vhd": {
|
|
||||||
"uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-0.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd"
|
|
||||||
},
|
|
||||||
"caching": "ReadWrite"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"lun": 1,
|
|
||||||
"name": "packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd",
|
|
||||||
"createOption": "Empty",
|
|
||||||
"image": {
|
|
||||||
"uri": "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd"
|
|
||||||
},
|
|
||||||
"vhd": {
|
|
||||||
"uri": "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-1.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd"
|
|
||||||
},
|
|
||||||
"caching": "ReadWrite"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"adminPassword": "[parameters('adminPassword')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[parameters('networkInterfaceId')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"provisioningState": 0
|
|
||||||
},
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"type": "Microsoft.Compute/virtualMachines",
|
|
||||||
"location": "southcentralus"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
||||||
var captureTemplate02 = `{
|
|
||||||
"operationId": "ac1c7c38-a591-41b3-89bd-ea39fceace1b",
|
|
||||||
"status": "Succeeded",
|
|
||||||
"startTime": "2016-04-04T21:07:25.2900874+00:00",
|
|
||||||
"endTime": "2016-04-04T21:07:26.4776321+00:00"
|
|
||||||
}`
|
|
||||||
|
|
||||||
func TestCaptureParseJson(t *testing.T) {
|
|
||||||
var operation CaptureOperation
|
|
||||||
err := json.Unmarshal([]byte(captureTemplate01), &operation)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to the sample capture operation: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testSubject := operation.Properties.Output
|
|
||||||
if testSubject.Schema != "http://schema.management.azure.com/schemas/2014-04-01-preview/VM_IP.json" {
|
|
||||||
t.Errorf("Schema's value was unexpected: %s", testSubject.Schema)
|
|
||||||
}
|
|
||||||
if testSubject.ContentVersion != "1.0.0.0" {
|
|
||||||
t.Errorf("ContentVersion's value was unexpected: %s", testSubject.ContentVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Parameters ====================================
|
|
||||||
if len(testSubject.Parameters) != 5 {
|
|
||||||
t.Fatalf("expected parameters to have 5 keys, but got %d", len(testSubject.Parameters))
|
|
||||||
}
|
|
||||||
if _, ok := testSubject.Parameters["vmName"]; !ok {
|
|
||||||
t.Errorf("Parameters['vmName'] was an expected parameters, but it did not exist")
|
|
||||||
}
|
|
||||||
if testSubject.Parameters["vmName"].Type != "string" {
|
|
||||||
t.Errorf("Parameters['vmName'].Type == 'string', but got '%s'", testSubject.Parameters["vmName"].Type)
|
|
||||||
}
|
|
||||||
if _, ok := testSubject.Parameters["vmSize"]; !ok {
|
|
||||||
t.Errorf("Parameters['vmSize'] was an expected parameters, but it did not exist")
|
|
||||||
}
|
|
||||||
if testSubject.Parameters["vmSize"].Type != "string" {
|
|
||||||
t.Errorf("Parameters['vmSize'].Type == 'string', but got '%s'", testSubject.Parameters["vmSize"])
|
|
||||||
}
|
|
||||||
if testSubject.Parameters["vmSize"].DefaultValue != "Standard_A2" {
|
|
||||||
t.Errorf("Parameters['vmSize'].DefaultValue == 'string', but got '%s'", testSubject.Parameters["vmSize"].DefaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Resources =====================================
|
|
||||||
if len(testSubject.Resources) != 1 {
|
|
||||||
t.Fatalf("expected resources to have length 1, but got %d", len(testSubject.Resources))
|
|
||||||
}
|
|
||||||
if testSubject.Resources[0].Name != "[parameters('vmName')]" {
|
|
||||||
t.Errorf("Resources[0].Name's value was unexpected: %s", testSubject.Resources[0].Name)
|
|
||||||
}
|
|
||||||
if testSubject.Resources[0].Type != "Microsoft.Compute/virtualMachines" {
|
|
||||||
t.Errorf("Resources[0].Type's value was unexpected: %s", testSubject.Resources[0].Type)
|
|
||||||
}
|
|
||||||
if testSubject.Resources[0].Location != "southcentralus" {
|
|
||||||
t.Errorf("Resources[0].Location's value was unexpected: %s", testSubject.Resources[0].Location)
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Resources/Properties =====================================
|
|
||||||
if testSubject.Resources[0].Properties.ProvisioningState != 0 {
|
|
||||||
t.Errorf("Resources[0].Properties.ProvisioningState's value was unexpected: %d", testSubject.Resources[0].Properties.ProvisioningState)
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Resources/Properties/HardwareProfile ======================
|
|
||||||
hardwareProfile := testSubject.Resources[0].Properties.HardwareProfile
|
|
||||||
if hardwareProfile.VMSize != "[parameters('vmSize')]" {
|
|
||||||
t.Errorf("Resources[0].Properties.HardwareProfile.VMSize's value was unexpected: %s", hardwareProfile.VMSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Resources/Properties/StorageProfile/OSDisk ================
|
|
||||||
osDisk := testSubject.Resources[0].Properties.StorageProfile.OSDisk
|
|
||||||
if osDisk.OSType != "Linux" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.OSDisk's value was unexpected: %s", osDisk.OSType)
|
|
||||||
}
|
|
||||||
if osDisk.Name != "packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Name's value was unexpected: %s", osDisk.Name)
|
|
||||||
}
|
|
||||||
if osDisk.CreateOption != "FromImage" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.CreateOption's value was unexpected: %s", osDisk.CreateOption)
|
|
||||||
}
|
|
||||||
if osDisk.Image.Uri != "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Image.Uri's value was unexpected: %s", osDisk.Image.Uri)
|
|
||||||
}
|
|
||||||
if osDisk.Vhd.Uri != "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/osDisk.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Vhd.Uri's value was unexpected: %s", osDisk.Vhd.Uri)
|
|
||||||
}
|
|
||||||
if osDisk.Caching != "ReadWrite" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.OSDisk.Caching's value was unexpected: %s", osDisk.Caching)
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Resources/Properties/StorageProfile/DataDisks ================
|
|
||||||
dataDisks := testSubject.Resources[0].Properties.StorageProfile.DataDisks
|
|
||||||
if len(dataDisks) != 2 {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.DataDisks, 2 disks expected but was: %d", len(dataDisks))
|
|
||||||
}
|
|
||||||
if dataDisks[0].Name != "packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Name's value was unexpected: %s", dataDisks[0].Name)
|
|
||||||
}
|
|
||||||
if dataDisks[0].CreateOption != "Empty" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].CreateOption's value was unexpected: %s", dataDisks[0].CreateOption)
|
|
||||||
}
|
|
||||||
if dataDisks[0].Image.Uri != "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-0.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Image.Uri's value was unexpected: %s", dataDisks[0].Image.Uri)
|
|
||||||
}
|
|
||||||
if dataDisks[0].Vhd.Uri != "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-0.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Vhd.Uri's value was unexpected: %s", dataDisks[0].Vhd.Uri)
|
|
||||||
}
|
|
||||||
if dataDisks[0].Caching != "ReadWrite" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[0].Caching's value was unexpected: %s", dataDisks[0].Caching)
|
|
||||||
}
|
|
||||||
if dataDisks[1].Name != "packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Name's value was unexpected: %s", dataDisks[1].Name)
|
|
||||||
}
|
|
||||||
if dataDisks[1].CreateOption != "Empty" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].CreateOption's value was unexpected: %s", dataDisks[1].CreateOption)
|
|
||||||
}
|
|
||||||
if dataDisks[1].Image.Uri != "http://storage.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-datadisk-1.32118633-6dc9-449f-83b6-a7d2983bec14.vhd" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Image.Uri's value was unexpected: %s", dataDisks[1].Image.Uri)
|
|
||||||
}
|
|
||||||
if dataDisks[1].Vhd.Uri != "http://storage.blob.core.windows.net/vmcontainerce1a1b75-f480-47cb-8e6e-55142e4a5f68/datadisk-1.ce1a1b75-f480-47cb-8e6e-55142e4a5f68.vhd" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Vhd.Uri's value was unexpected: %s", dataDisks[1].Vhd.Uri)
|
|
||||||
}
|
|
||||||
if dataDisks[1].Caching != "ReadWrite" {
|
|
||||||
t.Errorf("Resources[0].Properties.StorageProfile.dataDisks[1].Caching's value was unexpected: %s", dataDisks[1].Caching)
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Resources/Properties/OSProfile ============================
|
|
||||||
osProfile := testSubject.Resources[0].Properties.OSProfile
|
|
||||||
if osProfile.AdminPassword != "[parameters('adminPassword')]" {
|
|
||||||
t.Errorf("Resources[0].Properties.OSProfile.AdminPassword's value was unexpected: %s", osProfile.AdminPassword)
|
|
||||||
}
|
|
||||||
if osProfile.AdminUsername != "[parameters('adminUsername')]" {
|
|
||||||
t.Errorf("Resources[0].Properties.OSProfile.AdminUsername's value was unexpected: %s", osProfile.AdminUsername)
|
|
||||||
}
|
|
||||||
if osProfile.ComputerName != "[parameters('vmName')]" {
|
|
||||||
t.Errorf("Resources[0].Properties.OSProfile.ComputerName's value was unexpected: %s", osProfile.ComputerName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Resources/Properties/NetworkProfile =======================
|
|
||||||
networkProfile := testSubject.Resources[0].Properties.NetworkProfile
|
|
||||||
if len(networkProfile.NetworkInterfaces) != 1 {
|
|
||||||
t.Errorf("Count of Resources[0].Properties.NetworkProfile.NetworkInterfaces was expected to be 1, but go %d", len(networkProfile.NetworkInterfaces))
|
|
||||||
}
|
|
||||||
if networkProfile.NetworkInterfaces[0].Id != "[parameters('networkInterfaceId')]" {
|
|
||||||
t.Errorf("Resources[0].Properties.NetworkProfile.NetworkInterfaces[0].Id's value was unexpected: %s", networkProfile.NetworkInterfaces[0].Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Resources/Properties/DiagnosticsProfile ===================
|
|
||||||
diagnosticsProfile := testSubject.Resources[0].Properties.DiagnosticsProfile
|
|
||||||
if diagnosticsProfile.BootDiagnostics.Enabled != false {
|
|
||||||
t.Errorf("Resources[0].Properties.DiagnosticsProfile.BootDiagnostics.Enabled's value was unexpected: %t", diagnosticsProfile.BootDiagnostics.Enabled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCaptureEmptyOperationJson(t *testing.T) {
|
|
||||||
var operation CaptureOperation
|
|
||||||
err := json.Unmarshal([]byte(captureTemplate02), &operation)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to the sample capture operation: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if operation.Properties != nil {
|
|
||||||
t.Errorf("JSON contained no properties, but value was not nil: %+v", operation.Properties)
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,361 +0,0 @@
|
||||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FlatConfig is an auto-generated flat version of Config.
|
|
||||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
type FlatConfig struct {
|
|
||||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
|
||||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
|
||||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
|
||||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
|
||||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
|
||||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
|
||||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
|
||||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
|
||||||
CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name" hcl:"cloud_environment_name"`
|
|
||||||
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
|
|
||||||
ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"`
|
|
||||||
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"`
|
|
||||||
ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"`
|
|
||||||
ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"`
|
|
||||||
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
|
|
||||||
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
|
|
||||||
SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"`
|
|
||||||
UseAzureCLIAuth *bool `mapstructure:"use_azure_cli_auth" required:"false" cty:"use_azure_cli_auth" hcl:"use_azure_cli_auth"`
|
|
||||||
UserAssignedManagedIdentities []string `mapstructure:"user_assigned_managed_identities" required:"false" cty:"user_assigned_managed_identities" hcl:"user_assigned_managed_identities"`
|
|
||||||
CaptureNamePrefix *string `mapstructure:"capture_name_prefix" cty:"capture_name_prefix" hcl:"capture_name_prefix"`
|
|
||||||
CaptureContainerName *string `mapstructure:"capture_container_name" cty:"capture_container_name" hcl:"capture_container_name"`
|
|
||||||
SharedGallery *FlatSharedImageGallery `mapstructure:"shared_image_gallery" required:"false" cty:"shared_image_gallery" hcl:"shared_image_gallery"`
|
|
||||||
SharedGalleryDestination *FlatSharedImageGalleryDestination `mapstructure:"shared_image_gallery_destination" cty:"shared_image_gallery_destination" hcl:"shared_image_gallery_destination"`
|
|
||||||
SharedGalleryTimeout *string `mapstructure:"shared_image_gallery_timeout" cty:"shared_image_gallery_timeout" hcl:"shared_image_gallery_timeout"`
|
|
||||||
SharedGalleryImageVersionEndOfLifeDate *string `mapstructure:"shared_gallery_image_version_end_of_life_date" required:"false" cty:"shared_gallery_image_version_end_of_life_date" hcl:"shared_gallery_image_version_end_of_life_date"`
|
|
||||||
SharedGalleryImageVersionReplicaCount *int32 `mapstructure:"shared_image_gallery_replica_count" required:"false" cty:"shared_image_gallery_replica_count" hcl:"shared_image_gallery_replica_count"`
|
|
||||||
SharedGalleryImageVersionExcludeFromLatest *bool `mapstructure:"shared_gallery_image_version_exclude_from_latest" required:"false" cty:"shared_gallery_image_version_exclude_from_latest" hcl:"shared_gallery_image_version_exclude_from_latest"`
|
|
||||||
ImagePublisher *string `mapstructure:"image_publisher" required:"true" cty:"image_publisher" hcl:"image_publisher"`
|
|
||||||
ImageOffer *string `mapstructure:"image_offer" required:"true" cty:"image_offer" hcl:"image_offer"`
|
|
||||||
ImageSku *string `mapstructure:"image_sku" required:"true" cty:"image_sku" hcl:"image_sku"`
|
|
||||||
ImageVersion *string `mapstructure:"image_version" required:"false" cty:"image_version" hcl:"image_version"`
|
|
||||||
ImageUrl *string `mapstructure:"image_url" required:"true" cty:"image_url" hcl:"image_url"`
|
|
||||||
CustomManagedImageName *string `mapstructure:"custom_managed_image_name" required:"true" cty:"custom_managed_image_name" hcl:"custom_managed_image_name"`
|
|
||||||
CustomManagedImageResourceGroupName *string `mapstructure:"custom_managed_image_resource_group_name" required:"true" cty:"custom_managed_image_resource_group_name" hcl:"custom_managed_image_resource_group_name"`
|
|
||||||
Location *string `mapstructure:"location" cty:"location" hcl:"location"`
|
|
||||||
VMSize *string `mapstructure:"vm_size" required:"false" cty:"vm_size" hcl:"vm_size"`
|
|
||||||
ManagedImageResourceGroupName *string `mapstructure:"managed_image_resource_group_name" cty:"managed_image_resource_group_name" hcl:"managed_image_resource_group_name"`
|
|
||||||
ManagedImageName *string `mapstructure:"managed_image_name" cty:"managed_image_name" hcl:"managed_image_name"`
|
|
||||||
ManagedImageStorageAccountType *string `mapstructure:"managed_image_storage_account_type" required:"false" cty:"managed_image_storage_account_type" hcl:"managed_image_storage_account_type"`
|
|
||||||
ManagedImageOSDiskSnapshotName *string `mapstructure:"managed_image_os_disk_snapshot_name" required:"false" cty:"managed_image_os_disk_snapshot_name" hcl:"managed_image_os_disk_snapshot_name"`
|
|
||||||
ManagedImageDataDiskSnapshotPrefix *string `mapstructure:"managed_image_data_disk_snapshot_prefix" required:"false" cty:"managed_image_data_disk_snapshot_prefix" hcl:"managed_image_data_disk_snapshot_prefix"`
|
|
||||||
KeepOSDisk *bool `mapstructure:"keep_os_disk" required:"false" cty:"keep_os_disk" hcl:"keep_os_disk"`
|
|
||||||
ManagedImageZoneResilient *bool `mapstructure:"managed_image_zone_resilient" required:"false" cty:"managed_image_zone_resilient" hcl:"managed_image_zone_resilient"`
|
|
||||||
AzureTags map[string]string `mapstructure:"azure_tags" required:"false" cty:"azure_tags" hcl:"azure_tags"`
|
|
||||||
AzureTag []config.FlatNameValue `mapstructure:"azure_tag" required:"false" cty:"azure_tag" hcl:"azure_tag"`
|
|
||||||
ResourceGroupName *string `mapstructure:"resource_group_name" cty:"resource_group_name" hcl:"resource_group_name"`
|
|
||||||
StorageAccount *string `mapstructure:"storage_account" cty:"storage_account" hcl:"storage_account"`
|
|
||||||
TempComputeName *string `mapstructure:"temp_compute_name" required:"false" cty:"temp_compute_name" hcl:"temp_compute_name"`
|
|
||||||
TempNicName *string `mapstructure:"temp_nic_name" required:"false" cty:"temp_nic_name" hcl:"temp_nic_name"`
|
|
||||||
TempResourceGroupName *string `mapstructure:"temp_resource_group_name" cty:"temp_resource_group_name" hcl:"temp_resource_group_name"`
|
|
||||||
BuildResourceGroupName *string `mapstructure:"build_resource_group_name" cty:"build_resource_group_name" hcl:"build_resource_group_name"`
|
|
||||||
BuildKeyVaultName *string `mapstructure:"build_key_vault_name" cty:"build_key_vault_name" hcl:"build_key_vault_name"`
|
|
||||||
BuildKeyVaultSKU *string `mapstructure:"build_key_vault_sku" cty:"build_key_vault_sku" hcl:"build_key_vault_sku"`
|
|
||||||
PrivateVirtualNetworkWithPublicIp *bool `mapstructure:"private_virtual_network_with_public_ip" required:"false" cty:"private_virtual_network_with_public_ip" hcl:"private_virtual_network_with_public_ip"`
|
|
||||||
VirtualNetworkName *string `mapstructure:"virtual_network_name" required:"false" cty:"virtual_network_name" hcl:"virtual_network_name"`
|
|
||||||
VirtualNetworkSubnetName *string `mapstructure:"virtual_network_subnet_name" required:"false" cty:"virtual_network_subnet_name" hcl:"virtual_network_subnet_name"`
|
|
||||||
VirtualNetworkResourceGroupName *string `mapstructure:"virtual_network_resource_group_name" required:"false" cty:"virtual_network_resource_group_name" hcl:"virtual_network_resource_group_name"`
|
|
||||||
CustomDataFile *string `mapstructure:"custom_data_file" required:"false" cty:"custom_data_file" hcl:"custom_data_file"`
|
|
||||||
PlanInfo *FlatPlanInformation `mapstructure:"plan_info" required:"false" cty:"plan_info" hcl:"plan_info"`
|
|
||||||
PollingDurationTimeout *string `mapstructure:"polling_duration_timeout" required:"false" cty:"polling_duration_timeout" hcl:"polling_duration_timeout"`
|
|
||||||
OSType *string `mapstructure:"os_type" required:"false" cty:"os_type" hcl:"os_type"`
|
|
||||||
TempOSDiskName *string `mapstructure:"temp_os_disk_name" required:"false" cty:"temp_os_disk_name" hcl:"temp_os_disk_name"`
|
|
||||||
OSDiskSizeGB *int32 `mapstructure:"os_disk_size_gb" required:"false" cty:"os_disk_size_gb" hcl:"os_disk_size_gb"`
|
|
||||||
AdditionalDiskSize []int32 `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size" hcl:"disk_additional_size"`
|
|
||||||
DiskCachingType *string `mapstructure:"disk_caching_type" required:"false" cty:"disk_caching_type" hcl:"disk_caching_type"`
|
|
||||||
AllowedInboundIpAddresses []string `mapstructure:"allowed_inbound_ip_addresses" cty:"allowed_inbound_ip_addresses" hcl:"allowed_inbound_ip_addresses"`
|
|
||||||
BootDiagSTGAccount *string `mapstructure:"boot_diag_storage_account" required:"false" cty:"boot_diag_storage_account" hcl:"boot_diag_storage_account"`
|
|
||||||
CustomResourcePrefix *string `mapstructure:"custom_resource_build_prefix" required:"false" cty:"custom_resource_build_prefix" hcl:"custom_resource_build_prefix"`
|
|
||||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
|
||||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
|
||||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
|
||||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
|
||||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
|
||||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
|
||||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
|
||||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
|
||||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
|
||||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
|
||||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
|
||||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
|
||||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
|
||||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
|
||||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
|
||||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
|
||||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
|
||||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
|
||||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
|
||||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
|
||||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
|
||||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
|
||||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
|
||||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
|
||||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
|
||||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
|
||||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
|
||||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
|
||||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
|
||||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
|
||||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
|
||||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
|
||||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
|
||||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
|
||||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
|
||||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
|
||||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
|
||||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
|
||||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
|
||||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
|
||||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
|
||||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
|
||||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
|
||||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
|
||||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
|
||||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
|
||||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
|
||||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
|
||||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
|
||||||
AsyncResourceGroupDelete *bool `mapstructure:"async_resourcegroup_delete" required:"false" cty:"async_resourcegroup_delete" hcl:"async_resourcegroup_delete"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatConfig.
|
|
||||||
// FlatConfig is an auto-generated flat version of Config.
|
|
||||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
|
||||||
return new(FlatConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HCL2Spec returns the hcl spec of a Config.
|
|
||||||
// This spec is used by HCL to read the fields of Config.
|
|
||||||
// The decoded values from this spec will then be applied to a FlatConfig.
|
|
||||||
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|
||||||
s := map[string]hcldec.Spec{
|
|
||||||
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
|
|
||||||
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
|
|
||||||
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
|
|
||||||
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
|
|
||||||
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
|
|
||||||
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
|
|
||||||
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
|
|
||||||
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
|
||||||
"cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false},
|
|
||||||
"client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false},
|
|
||||||
"client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false},
|
|
||||||
"client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false},
|
|
||||||
"client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false},
|
|
||||||
"client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false},
|
|
||||||
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
|
||||||
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
|
||||||
"subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false},
|
|
||||||
"use_azure_cli_auth": &hcldec.AttrSpec{Name: "use_azure_cli_auth", Type: cty.Bool, Required: false},
|
|
||||||
"user_assigned_managed_identities": &hcldec.AttrSpec{Name: "user_assigned_managed_identities", Type: cty.List(cty.String), Required: false},
|
|
||||||
"capture_name_prefix": &hcldec.AttrSpec{Name: "capture_name_prefix", Type: cty.String, Required: false},
|
|
||||||
"capture_container_name": &hcldec.AttrSpec{Name: "capture_container_name", Type: cty.String, Required: false},
|
|
||||||
"shared_image_gallery": &hcldec.BlockSpec{TypeName: "shared_image_gallery", Nested: hcldec.ObjectSpec((*FlatSharedImageGallery)(nil).HCL2Spec())},
|
|
||||||
"shared_image_gallery_destination": &hcldec.BlockSpec{TypeName: "shared_image_gallery_destination", Nested: hcldec.ObjectSpec((*FlatSharedImageGalleryDestination)(nil).HCL2Spec())},
|
|
||||||
"shared_image_gallery_timeout": &hcldec.AttrSpec{Name: "shared_image_gallery_timeout", Type: cty.String, Required: false},
|
|
||||||
"shared_gallery_image_version_end_of_life_date": &hcldec.AttrSpec{Name: "shared_gallery_image_version_end_of_life_date", Type: cty.String, Required: false},
|
|
||||||
"shared_image_gallery_replica_count": &hcldec.AttrSpec{Name: "shared_image_gallery_replica_count", Type: cty.Number, Required: false},
|
|
||||||
"shared_gallery_image_version_exclude_from_latest": &hcldec.AttrSpec{Name: "shared_gallery_image_version_exclude_from_latest", Type: cty.Bool, Required: false},
|
|
||||||
"image_publisher": &hcldec.AttrSpec{Name: "image_publisher", Type: cty.String, Required: false},
|
|
||||||
"image_offer": &hcldec.AttrSpec{Name: "image_offer", Type: cty.String, Required: false},
|
|
||||||
"image_sku": &hcldec.AttrSpec{Name: "image_sku", Type: cty.String, Required: false},
|
|
||||||
"image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false},
|
|
||||||
"image_url": &hcldec.AttrSpec{Name: "image_url", Type: cty.String, Required: false},
|
|
||||||
"custom_managed_image_name": &hcldec.AttrSpec{Name: "custom_managed_image_name", Type: cty.String, Required: false},
|
|
||||||
"custom_managed_image_resource_group_name": &hcldec.AttrSpec{Name: "custom_managed_image_resource_group_name", Type: cty.String, Required: false},
|
|
||||||
"location": &hcldec.AttrSpec{Name: "location", Type: cty.String, Required: false},
|
|
||||||
"vm_size": &hcldec.AttrSpec{Name: "vm_size", Type: cty.String, Required: false},
|
|
||||||
"managed_image_resource_group_name": &hcldec.AttrSpec{Name: "managed_image_resource_group_name", Type: cty.String, Required: false},
|
|
||||||
"managed_image_name": &hcldec.AttrSpec{Name: "managed_image_name", Type: cty.String, Required: false},
|
|
||||||
"managed_image_storage_account_type": &hcldec.AttrSpec{Name: "managed_image_storage_account_type", Type: cty.String, Required: false},
|
|
||||||
"managed_image_os_disk_snapshot_name": &hcldec.AttrSpec{Name: "managed_image_os_disk_snapshot_name", Type: cty.String, Required: false},
|
|
||||||
"managed_image_data_disk_snapshot_prefix": &hcldec.AttrSpec{Name: "managed_image_data_disk_snapshot_prefix", Type: cty.String, Required: false},
|
|
||||||
"keep_os_disk": &hcldec.AttrSpec{Name: "keep_os_disk", Type: cty.Bool, Required: false},
|
|
||||||
"managed_image_zone_resilient": &hcldec.AttrSpec{Name: "managed_image_zone_resilient", Type: cty.Bool, Required: false},
|
|
||||||
"azure_tags": &hcldec.AttrSpec{Name: "azure_tags", Type: cty.Map(cty.String), Required: false},
|
|
||||||
"azure_tag": &hcldec.BlockListSpec{TypeName: "azure_tag", Nested: hcldec.ObjectSpec((*config.FlatNameValue)(nil).HCL2Spec())},
|
|
||||||
"resource_group_name": &hcldec.AttrSpec{Name: "resource_group_name", Type: cty.String, Required: false},
|
|
||||||
"storage_account": &hcldec.AttrSpec{Name: "storage_account", Type: cty.String, Required: false},
|
|
||||||
"temp_compute_name": &hcldec.AttrSpec{Name: "temp_compute_name", Type: cty.String, Required: false},
|
|
||||||
"temp_nic_name": &hcldec.AttrSpec{Name: "temp_nic_name", Type: cty.String, Required: false},
|
|
||||||
"temp_resource_group_name": &hcldec.AttrSpec{Name: "temp_resource_group_name", Type: cty.String, Required: false},
|
|
||||||
"build_resource_group_name": &hcldec.AttrSpec{Name: "build_resource_group_name", Type: cty.String, Required: false},
|
|
||||||
"build_key_vault_name": &hcldec.AttrSpec{Name: "build_key_vault_name", Type: cty.String, Required: false},
|
|
||||||
"build_key_vault_sku": &hcldec.AttrSpec{Name: "build_key_vault_sku", Type: cty.String, Required: false},
|
|
||||||
"private_virtual_network_with_public_ip": &hcldec.AttrSpec{Name: "private_virtual_network_with_public_ip", Type: cty.Bool, Required: false},
|
|
||||||
"virtual_network_name": &hcldec.AttrSpec{Name: "virtual_network_name", Type: cty.String, Required: false},
|
|
||||||
"virtual_network_subnet_name": &hcldec.AttrSpec{Name: "virtual_network_subnet_name", Type: cty.String, Required: false},
|
|
||||||
"virtual_network_resource_group_name": &hcldec.AttrSpec{Name: "virtual_network_resource_group_name", Type: cty.String, Required: false},
|
|
||||||
"custom_data_file": &hcldec.AttrSpec{Name: "custom_data_file", Type: cty.String, Required: false},
|
|
||||||
"plan_info": &hcldec.BlockSpec{TypeName: "plan_info", Nested: hcldec.ObjectSpec((*FlatPlanInformation)(nil).HCL2Spec())},
|
|
||||||
"polling_duration_timeout": &hcldec.AttrSpec{Name: "polling_duration_timeout", Type: cty.String, Required: false},
|
|
||||||
"os_type": &hcldec.AttrSpec{Name: "os_type", Type: cty.String, Required: false},
|
|
||||||
"temp_os_disk_name": &hcldec.AttrSpec{Name: "temp_os_disk_name", Type: cty.String, Required: false},
|
|
||||||
"os_disk_size_gb": &hcldec.AttrSpec{Name: "os_disk_size_gb", Type: cty.Number, Required: false},
|
|
||||||
"disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.Number), Required: false},
|
|
||||||
"disk_caching_type": &hcldec.AttrSpec{Name: "disk_caching_type", Type: cty.String, Required: false},
|
|
||||||
"allowed_inbound_ip_addresses": &hcldec.AttrSpec{Name: "allowed_inbound_ip_addresses", Type: cty.List(cty.String), Required: false},
|
|
||||||
"boot_diag_storage_account": &hcldec.AttrSpec{Name: "boot_diag_storage_account", Type: cty.String, Required: false},
|
|
||||||
"custom_resource_build_prefix": &hcldec.AttrSpec{Name: "custom_resource_build_prefix", Type: cty.String, Required: false},
|
|
||||||
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
|
||||||
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
|
||||||
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
|
||||||
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
|
|
||||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
|
||||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
|
||||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
|
||||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
|
||||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
|
||||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
|
||||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
|
||||||
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
|
||||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
|
||||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
|
||||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
|
||||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
|
||||||
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
|
|
||||||
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
|
|
||||||
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
|
|
||||||
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
|
|
||||||
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
|
|
||||||
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
|
|
||||||
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
|
|
||||||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
|
||||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
|
||||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
|
||||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
|
||||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
|
||||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
|
||||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
|
||||||
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
|
|
||||||
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
|
|
||||||
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
|
|
||||||
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
|
|
||||||
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
|
|
||||||
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
|
|
||||||
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
|
|
||||||
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
|
|
||||||
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
|
|
||||||
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
|
|
||||||
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
|
|
||||||
"winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false},
|
|
||||||
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
|
|
||||||
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
|
|
||||||
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
|
|
||||||
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
|
||||||
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
|
||||||
"async_resourcegroup_delete": &hcldec.AttrSpec{Name: "async_resourcegroup_delete", Type: cty.Bool, Required: false},
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatPlanInformation is an auto-generated flat version of PlanInformation.
|
|
||||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
type FlatPlanInformation struct {
|
|
||||||
PlanName *string `mapstructure:"plan_name" cty:"plan_name" hcl:"plan_name"`
|
|
||||||
PlanProduct *string `mapstructure:"plan_product" cty:"plan_product" hcl:"plan_product"`
|
|
||||||
PlanPublisher *string `mapstructure:"plan_publisher" cty:"plan_publisher" hcl:"plan_publisher"`
|
|
||||||
PlanPromotionCode *string `mapstructure:"plan_promotion_code" cty:"plan_promotion_code" hcl:"plan_promotion_code"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatPlanInformation.
|
|
||||||
// FlatPlanInformation is an auto-generated flat version of PlanInformation.
|
|
||||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
func (*PlanInformation) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
|
||||||
return new(FlatPlanInformation)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HCL2Spec returns the hcl spec of a PlanInformation.
|
|
||||||
// This spec is used by HCL to read the fields of PlanInformation.
|
|
||||||
// The decoded values from this spec will then be applied to a FlatPlanInformation.
|
|
||||||
func (*FlatPlanInformation) HCL2Spec() map[string]hcldec.Spec {
|
|
||||||
s := map[string]hcldec.Spec{
|
|
||||||
"plan_name": &hcldec.AttrSpec{Name: "plan_name", Type: cty.String, Required: false},
|
|
||||||
"plan_product": &hcldec.AttrSpec{Name: "plan_product", Type: cty.String, Required: false},
|
|
||||||
"plan_publisher": &hcldec.AttrSpec{Name: "plan_publisher", Type: cty.String, Required: false},
|
|
||||||
"plan_promotion_code": &hcldec.AttrSpec{Name: "plan_promotion_code", Type: cty.String, Required: false},
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatSharedImageGallery is an auto-generated flat version of SharedImageGallery.
|
|
||||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
type FlatSharedImageGallery struct {
|
|
||||||
Subscription *string `mapstructure:"subscription" cty:"subscription" hcl:"subscription"`
|
|
||||||
ResourceGroup *string `mapstructure:"resource_group" cty:"resource_group" hcl:"resource_group"`
|
|
||||||
GalleryName *string `mapstructure:"gallery_name" cty:"gallery_name" hcl:"gallery_name"`
|
|
||||||
ImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"`
|
|
||||||
ImageVersion *string `mapstructure:"image_version" required:"false" cty:"image_version" hcl:"image_version"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatSharedImageGallery.
|
|
||||||
// FlatSharedImageGallery is an auto-generated flat version of SharedImageGallery.
|
|
||||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
func (*SharedImageGallery) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
|
||||||
return new(FlatSharedImageGallery)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HCL2Spec returns the hcl spec of a SharedImageGallery.
|
|
||||||
// This spec is used by HCL to read the fields of SharedImageGallery.
|
|
||||||
// The decoded values from this spec will then be applied to a FlatSharedImageGallery.
|
|
||||||
func (*FlatSharedImageGallery) HCL2Spec() map[string]hcldec.Spec {
|
|
||||||
s := map[string]hcldec.Spec{
|
|
||||||
"subscription": &hcldec.AttrSpec{Name: "subscription", Type: cty.String, Required: false},
|
|
||||||
"resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false},
|
|
||||||
"gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false},
|
|
||||||
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
|
||||||
"image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false},
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination.
|
|
||||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
type FlatSharedImageGalleryDestination struct {
|
|
||||||
SigDestinationSubscription *string `mapstructure:"subscription" cty:"subscription" hcl:"subscription"`
|
|
||||||
SigDestinationResourceGroup *string `mapstructure:"resource_group" cty:"resource_group" hcl:"resource_group"`
|
|
||||||
SigDestinationGalleryName *string `mapstructure:"gallery_name" cty:"gallery_name" hcl:"gallery_name"`
|
|
||||||
SigDestinationImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"`
|
|
||||||
SigDestinationImageVersion *string `mapstructure:"image_version" cty:"image_version" hcl:"image_version"`
|
|
||||||
SigDestinationReplicationRegions []string `mapstructure:"replication_regions" cty:"replication_regions" hcl:"replication_regions"`
|
|
||||||
SigDestinationStorageAccountType *string `mapstructure:"storage_account_type" cty:"storage_account_type" hcl:"storage_account_type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatSharedImageGalleryDestination.
|
|
||||||
// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination.
|
|
||||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
|
||||||
return new(FlatSharedImageGalleryDestination)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HCL2Spec returns the hcl spec of a SharedImageGalleryDestination.
|
|
||||||
// This spec is used by HCL to read the fields of SharedImageGalleryDestination.
|
|
||||||
// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination.
|
|
||||||
func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec {
|
|
||||||
s := map[string]hcldec.Spec{
|
|
||||||
"subscription": &hcldec.AttrSpec{Name: "subscription", Type: cty.String, Required: false},
|
|
||||||
"resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false},
|
|
||||||
"gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false},
|
|
||||||
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
|
||||||
"image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false},
|
|
||||||
"replication_regions": &hcldec.AttrSpec{Name: "replication_regions", Type: cty.List(cty.String), Required: false},
|
|
||||||
"storage_account_type": &hcldec.AttrSpec{Name: "storage_account_type", Type: cty.String, Required: false},
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,71 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/Azure/go-autorest/autorest"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/logutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
func chop(data []byte, maxlen int64) string {
|
|
||||||
s := string(data)
|
|
||||||
if int64(len(s)) > maxlen {
|
|
||||||
s = s[:maxlen] + "..."
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleBody(body io.ReadCloser, maxlen int64) (io.ReadCloser, string) {
|
|
||||||
if body == nil {
|
|
||||||
return nil, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
defer body.Close()
|
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return ioutil.NopCloser(bytes.NewReader(b)), chop(b, maxlen)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withInspection(maxlen int64) autorest.PrepareDecorator {
|
|
||||||
return func(p autorest.Preparer) autorest.Preparer {
|
|
||||||
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
|
||||||
body, bodyString := handleBody(r.Body, maxlen)
|
|
||||||
r.Body = body
|
|
||||||
|
|
||||||
log.Print("Azure request", logutil.Fields{
|
|
||||||
"method": r.Method,
|
|
||||||
"request": r.URL.String(),
|
|
||||||
"body": bodyString,
|
|
||||||
})
|
|
||||||
return p.Prepare(r)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func byInspecting(maxlen int64) autorest.RespondDecorator {
|
|
||||||
return func(r autorest.Responder) autorest.Responder {
|
|
||||||
return autorest.ResponderFunc(func(resp *http.Response) error {
|
|
||||||
body, bodyString := handleBody(resp.Body, maxlen)
|
|
||||||
resp.Body = body
|
|
||||||
|
|
||||||
log.Print("Azure response", logutil.Fields{
|
|
||||||
"status": resp.Status,
|
|
||||||
"method": resp.Request.Method,
|
|
||||||
"request": resp.Request.URL.String(),
|
|
||||||
"x-ms-request-id": azure.ExtractRequestID(resp),
|
|
||||||
"body": bodyString,
|
|
||||||
})
|
|
||||||
return r.Respond(resp)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
KeySize = 2048
|
|
||||||
)
|
|
||||||
|
|
||||||
type OpenSshKeyPair struct {
|
|
||||||
privateKey *rsa.PrivateKey
|
|
||||||
publicKey ssh.PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewOpenSshKeyPair() (*OpenSshKeyPair, error) {
|
|
||||||
return NewOpenSshKeyPairWithSize(KeySize)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewOpenSshKeyPairWithSize(keySize int) (*OpenSshKeyPair, error) {
|
|
||||||
privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &OpenSshKeyPair{
|
|
||||||
privateKey: privateKey,
|
|
||||||
publicKey: publicKey,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *OpenSshKeyPair) AuthorizedKey() string {
|
|
||||||
return fmt.Sprintf("%s %s packer Azure Deployment%s",
|
|
||||||
s.publicKey.Type(),
|
|
||||||
base64.StdEncoding.EncodeToString(s.publicKey.Marshal()),
|
|
||||||
time.Now().Format(time.RFC3339))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *OpenSshKeyPair) PrivateKey() []byte {
|
|
||||||
privateKey := pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "RSA PRIVATE KEY",
|
|
||||||
Bytes: x509.MarshalPKCS1PrivateKey(s.privateKey),
|
|
||||||
})
|
|
||||||
|
|
||||||
return privateKey
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFart(t *testing.T) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizedKeyShouldParse(t *testing.T) {
|
|
||||||
testSubject, err := NewOpenSshKeyPairWithSize(512)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create a new OpenSSH key pair, err=%s.", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
authorizedKey := testSubject.AuthorizedKey()
|
|
||||||
|
|
||||||
_, _, _, _, err = ssh.ParseAuthorizedKey([]byte(authorizedKey))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to parse the authorized key, err=%s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrivateKeyShouldParse(t *testing.T) {
|
|
||||||
testSubject, err := NewOpenSshKeyPairWithSize(512)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create a new OpenSSH key pair, err=%s.", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ssh.ParsePrivateKey([]byte(testSubject.PrivateKey()))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to parse the private key, err=%s\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,141 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
// Code to resolve resources that are required by the API. These resources
|
|
||||||
// can most likely be resolved without asking the user, thereby reducing the
|
|
||||||
// amount of configuration they need to provide.
|
|
||||||
//
|
|
||||||
// Resource resolver differs from config retriever because resource resolver
|
|
||||||
// requires a client to communicate with the Azure API. A config retriever is
|
|
||||||
// used to determine values without use of a client.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
|
||||||
)
|
|
||||||
|
|
||||||
type resourceResolver struct {
|
|
||||||
client *AzureClient
|
|
||||||
findVirtualNetworkResourceGroup func(*AzureClient, string) (string, error)
|
|
||||||
findVirtualNetworkSubnet func(*AzureClient, string, string) (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newResourceResolver(client *AzureClient) *resourceResolver {
|
|
||||||
return &resourceResolver{
|
|
||||||
client: client,
|
|
||||||
findVirtualNetworkResourceGroup: findVirtualNetworkResourceGroup,
|
|
||||||
findVirtualNetworkSubnet: findVirtualNetworkSubnet,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *resourceResolver) Resolve(c *Config) error {
|
|
||||||
if s.shouldResolveResourceGroup(c) {
|
|
||||||
resourceGroupName, err := s.findVirtualNetworkResourceGroup(s.client, c.VirtualNetworkName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
subnetName, err := s.findVirtualNetworkSubnet(s.client, resourceGroupName, c.VirtualNetworkName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.VirtualNetworkResourceGroupName = resourceGroupName
|
|
||||||
c.VirtualNetworkSubnetName = subnetName
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.shouldResolveManagedImageName(c) {
|
|
||||||
image, err := findManagedImageByName(s.client, c.CustomManagedImageName, c.CustomManagedImageResourceGroupName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.customManagedImageID = *image.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *resourceResolver) shouldResolveResourceGroup(c *Config) bool {
|
|
||||||
return c.VirtualNetworkName != "" && c.VirtualNetworkResourceGroupName == ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *resourceResolver) shouldResolveManagedImageName(c *Config) bool {
|
|
||||||
return c.CustomManagedImageName != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func getResourceGroupNameFromId(id string) string {
|
|
||||||
// "/subscriptions/3f499422-dd76-4114-8859-86d526c9deb6/resourceGroups/packer-Resource-Group-yylnwsl30j/providers/...
|
|
||||||
xs := strings.Split(id, "/")
|
|
||||||
return xs[4]
|
|
||||||
}
|
|
||||||
|
|
||||||
func findManagedImageByName(client *AzureClient, name, resourceGroupName string) (*compute.Image, error) {
|
|
||||||
images, err := client.ImagesClient.ListByResourceGroupComplete(context.TODO(), resourceGroupName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for images.NotDone() {
|
|
||||||
image := images.Value()
|
|
||||||
if strings.EqualFold(name, *image.Name) {
|
|
||||||
return &image, nil
|
|
||||||
}
|
|
||||||
if err = images.Next(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("Cannot find an image named '%s' in the resource group '%s'", name, resourceGroupName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func findVirtualNetworkResourceGroup(client *AzureClient, name string) (string, error) {
|
|
||||||
virtualNetworks, err := client.VirtualNetworksClient.ListAllComplete(context.TODO())
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceGroupNames := make([]string, 0)
|
|
||||||
for virtualNetworks.NotDone() {
|
|
||||||
virtualNetwork := virtualNetworks.Value()
|
|
||||||
if strings.EqualFold(name, *virtualNetwork.Name) {
|
|
||||||
rgn := getResourceGroupNameFromId(*virtualNetwork.ID)
|
|
||||||
resourceGroupNames = append(resourceGroupNames, rgn)
|
|
||||||
}
|
|
||||||
if err = virtualNetworks.Next(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(resourceGroupNames) == 0 {
|
|
||||||
return "", fmt.Errorf("Cannot find a resource group with a virtual network called %q", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(resourceGroupNames) > 1 {
|
|
||||||
return "", fmt.Errorf("Found multiple resource groups with a virtual network called %q, please use virtual_network_subnet_name and virtual_network_resource_group_name to disambiguate", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resourceGroupNames[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findVirtualNetworkSubnet(client *AzureClient, resourceGroupName string, name string) (string, error) {
|
|
||||||
subnets, err := client.SubnetsClient.List(context.TODO(), resourceGroupName, name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
subnetList := subnets.Values() // only first page of subnets, but only interested in ==0 or >1
|
|
||||||
|
|
||||||
if len(subnetList) == 0 {
|
|
||||||
return "", fmt.Errorf("Cannot find a subnet in the resource group %q associated with the virtual network called %q", resourceGroupName, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(subnetList) > 1 {
|
|
||||||
return "", fmt.Errorf("Found multiple subnets in the resource group %q associated with the virtual network called %q, please use virtual_network_subnet_name and virtual_network_resource_group_name to disambiguate", resourceGroupName, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
subnet := subnetList[0]
|
|
||||||
return *subnet.Name, nil
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestResourceResolverIgnoresEmptyVirtualNetworkName(t *testing.T) {
|
|
||||||
var c Config
|
|
||||||
c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration())
|
|
||||||
if c.VirtualNetworkName != "" {
|
|
||||||
t.Fatalf("Expected VirtualNetworkName to be empty by default")
|
|
||||||
}
|
|
||||||
|
|
||||||
sut := newTestResourceResolver()
|
|
||||||
sut.findVirtualNetworkResourceGroup = nil // assert that this is not even called
|
|
||||||
sut.Resolve(&c)
|
|
||||||
|
|
||||||
if c.VirtualNetworkName != "" {
|
|
||||||
t.Fatalf("Expected VirtualNetworkName to be empty")
|
|
||||||
}
|
|
||||||
if c.VirtualNetworkResourceGroupName != "" {
|
|
||||||
t.Fatalf("Expected VirtualNetworkResourceGroupName to be empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user fully specified the virtual network name and resource group then
|
|
||||||
// there is no need to do a lookup.
|
|
||||||
func TestResourceResolverIgnoresSetVirtualNetwork(t *testing.T) {
|
|
||||||
var c Config
|
|
||||||
c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration())
|
|
||||||
c.VirtualNetworkName = "--virtual-network-name--"
|
|
||||||
c.VirtualNetworkResourceGroupName = "--virtual-network-resource-group-name--"
|
|
||||||
c.VirtualNetworkSubnetName = "--virtual-network-subnet-name--"
|
|
||||||
|
|
||||||
sut := newTestResourceResolver()
|
|
||||||
sut.findVirtualNetworkResourceGroup = nil // assert that this is not even called
|
|
||||||
sut.findVirtualNetworkSubnet = nil // assert that this is not even called
|
|
||||||
sut.Resolve(&c)
|
|
||||||
|
|
||||||
if c.VirtualNetworkName != "--virtual-network-name--" {
|
|
||||||
t.Fatalf("Expected VirtualNetworkName to be --virtual-network-name--")
|
|
||||||
}
|
|
||||||
if c.VirtualNetworkResourceGroupName != "--virtual-network-resource-group-name--" {
|
|
||||||
t.Fatalf("Expected VirtualNetworkResourceGroupName to be --virtual-network-resource-group-name--")
|
|
||||||
}
|
|
||||||
if c.VirtualNetworkSubnetName != "--virtual-network-subnet-name--" {
|
|
||||||
t.Fatalf("Expected VirtualNetworkSubnetName to be --virtual-network-subnet-name--")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user set virtual network name then the code should resolve virtual network
|
|
||||||
// resource group name.
|
|
||||||
func TestResourceResolverSetVirtualNetworkResourceGroupName(t *testing.T) {
|
|
||||||
var c Config
|
|
||||||
c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration())
|
|
||||||
c.VirtualNetworkName = "--virtual-network-name--"
|
|
||||||
|
|
||||||
sut := newTestResourceResolver()
|
|
||||||
sut.Resolve(&c)
|
|
||||||
|
|
||||||
if c.VirtualNetworkResourceGroupName != "findVirtualNetworkResourceGroup is mocked" {
|
|
||||||
t.Fatalf("Expected VirtualNetworkResourceGroupName to be 'findVirtualNetworkResourceGroup is mocked'")
|
|
||||||
}
|
|
||||||
if c.VirtualNetworkSubnetName != "findVirtualNetworkSubnet is mocked" {
|
|
||||||
t.Fatalf("Expected findVirtualNetworkSubnet to be 'findVirtualNetworkSubnet is mocked'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestResourceResolver() resourceResolver {
|
|
||||||
return resourceResolver{
|
|
||||||
client: nil,
|
|
||||||
findVirtualNetworkResourceGroup: func(*AzureClient, string) (string, error) {
|
|
||||||
return "findVirtualNetworkResourceGroup is mocked", nil
|
|
||||||
},
|
|
||||||
findVirtualNetworkSubnet: func(*AzureClient, string, string) (string, error) {
|
|
||||||
return "findVirtualNetworkSubnet is mocked", nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func processStepResult(
|
|
||||||
err error, sayError func(error), state multistep.StateBag) multistep.StepAction {
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
state.Put(constants.Error, err)
|
|
||||||
sayError(err)
|
|
||||||
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepCaptureImage struct {
|
|
||||||
client *AzureClient
|
|
||||||
generalizeVM func(resourceGroupName, computeName string) error
|
|
||||||
captureVhd func(ctx context.Context, resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters) error
|
|
||||||
captureManagedImage func(ctx context.Context, resourceGroupName string, computeName string, parameters *compute.Image) error
|
|
||||||
get func(client *AzureClient) *CaptureTemplate
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepCaptureImage(client *AzureClient, ui packersdk.Ui) *StepCaptureImage {
|
|
||||||
var step = &StepCaptureImage{
|
|
||||||
client: client,
|
|
||||||
get: func(client *AzureClient) *CaptureTemplate {
|
|
||||||
return client.Template
|
|
||||||
},
|
|
||||||
say: func(message string) {
|
|
||||||
ui.Say(message)
|
|
||||||
},
|
|
||||||
error: func(e error) {
|
|
||||||
ui.Error(e.Error())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
step.generalizeVM = step.generalize
|
|
||||||
step.captureVhd = step.captureImage
|
|
||||||
step.captureManagedImage = step.captureImageFromVM
|
|
||||||
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCaptureImage) generalize(resourceGroupName string, computeName string) error {
|
|
||||||
_, err := s.client.Generalize(context.TODO(), resourceGroupName, computeName)
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCaptureImage) captureImageFromVM(ctx context.Context, resourceGroupName string, imageName string, image *compute.Image) error {
|
|
||||||
f, err := s.client.ImagesClient.CreateOrUpdate(ctx, resourceGroupName, imageName, *image)
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
return f.WaitForCompletionRef(ctx, s.client.ImagesClient.Client)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCaptureImage) captureImage(ctx context.Context, resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters) error {
|
|
||||||
f, err := s.client.VirtualMachinesClient.Capture(ctx, resourceGroupName, computeName, *parameters)
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
return f.WaitForCompletionRef(ctx, s.client.VirtualMachinesClient.Client)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCaptureImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
s.say("Capturing image ...")
|
|
||||||
|
|
||||||
var computeName = state.Get(constants.ArmComputeName).(string)
|
|
||||||
var location = state.Get(constants.ArmLocation).(string)
|
|
||||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
var vmCaptureParameters = state.Get(constants.ArmVirtualMachineCaptureParameters).(*compute.VirtualMachineCaptureParameters)
|
|
||||||
var imageParameters = state.Get(constants.ArmImageParameters).(*compute.Image)
|
|
||||||
|
|
||||||
var isManagedImage = state.Get(constants.ArmIsManagedImage).(bool)
|
|
||||||
var targetManagedImageResourceGroupName = state.Get(constants.ArmManagedImageResourceGroupName).(string)
|
|
||||||
var targetManagedImageName = state.Get(constants.ArmManagedImageName).(string)
|
|
||||||
var targetManagedImageLocation = state.Get(constants.ArmLocation).(string)
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> Compute ResourceGroupName : '%s'", resourceGroupName))
|
|
||||||
s.say(fmt.Sprintf(" -> Compute Name : '%s'", computeName))
|
|
||||||
s.say(fmt.Sprintf(" -> Compute Location : '%s'", location))
|
|
||||||
|
|
||||||
err := s.generalizeVM(resourceGroupName, computeName)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
if isManagedImage {
|
|
||||||
s.say(fmt.Sprintf(" -> Image ResourceGroupName : '%s'", targetManagedImageResourceGroupName))
|
|
||||||
s.say(fmt.Sprintf(" -> Image Name : '%s'", targetManagedImageName))
|
|
||||||
s.say(fmt.Sprintf(" -> Image Location : '%s'", targetManagedImageLocation))
|
|
||||||
err = s.captureManagedImage(ctx, targetManagedImageResourceGroupName, targetManagedImageName, imageParameters)
|
|
||||||
} else {
|
|
||||||
err = s.captureVhd(ctx, resourceGroupName, computeName, vmCaptureParameters)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
state.Put(constants.Error, err)
|
|
||||||
s.error(err)
|
|
||||||
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK(chrboum): I do not like this. The capture method should be returning this value
|
|
||||||
// instead having to pass in another lambda.
|
|
||||||
//
|
|
||||||
// Having to resort to capturing the template via an inspector is hack, and once I can
|
|
||||||
// resolve that I can cleanup this code too. See the comments in azure_client.go for more
|
|
||||||
// details.
|
|
||||||
// [paulmey]: autorest.Future now has access to the last http.Response, but I'm not sure if
|
|
||||||
// the body is still accessible.
|
|
||||||
template := s.get(s.client)
|
|
||||||
state.Put(constants.ArmCaptureTemplate, template)
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepCaptureImage) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,137 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepCaptureImageShouldFailIfCaptureFails(t *testing.T) {
|
|
||||||
var testSubject = &StepCaptureImage{
|
|
||||||
captureVhd: func(context.Context, string, string, *compute.VirtualMachineCaptureParameters) error {
|
|
||||||
return fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
generalizeVM: func(string, string) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
get: func(client *AzureClient) *CaptureTemplate {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepCaptureImage()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCaptureImageShouldPassIfCapturePasses(t *testing.T) {
|
|
||||||
var testSubject = &StepCaptureImage{
|
|
||||||
captureVhd: func(context.Context, string, string, *compute.VirtualMachineCaptureParameters) error { return nil },
|
|
||||||
generalizeVM: func(string, string) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
get: func(client *AzureClient) *CaptureTemplate {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepCaptureImage()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|
||||||
cancelCh := make(chan<- struct{})
|
|
||||||
defer close(cancelCh)
|
|
||||||
|
|
||||||
var actualResourceGroupName string
|
|
||||||
var actualComputeName string
|
|
||||||
var actualVirtualMachineCaptureParameters *compute.VirtualMachineCaptureParameters
|
|
||||||
actualCaptureTemplate := &CaptureTemplate{
|
|
||||||
Schema: "!! Unit Test !!",
|
|
||||||
}
|
|
||||||
|
|
||||||
var testSubject = &StepCaptureImage{
|
|
||||||
captureVhd: func(ctx context.Context, resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters) error {
|
|
||||||
actualResourceGroupName = resourceGroupName
|
|
||||||
actualComputeName = computeName
|
|
||||||
actualVirtualMachineCaptureParameters = parameters
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
generalizeVM: func(string, string) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
get: func(client *AzureClient) *CaptureTemplate {
|
|
||||||
return actualCaptureTemplate
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepCaptureImage()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string)
|
|
||||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
var expectedVirtualMachineCaptureParameters = stateBag.Get(constants.ArmVirtualMachineCaptureParameters).(*compute.VirtualMachineCaptureParameters)
|
|
||||||
var expectedCaptureTemplate = stateBag.Get(constants.ArmCaptureTemplate).(*CaptureTemplate)
|
|
||||||
|
|
||||||
if actualComputeName != expectedComputeName {
|
|
||||||
t.Fatal("Expected StepCaptureImage to source 'constants.ArmComputeName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualResourceGroupName != expectedResourceGroupName {
|
|
||||||
t.Fatal("Expected StepCaptureImage to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualVirtualMachineCaptureParameters != expectedVirtualMachineCaptureParameters {
|
|
||||||
t.Fatal("Expected StepCaptureImage to source 'constants.ArmVirtualMachineCaptureParameters' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualCaptureTemplate != expectedCaptureTemplate {
|
|
||||||
t.Fatal("Expected StepCaptureImage to source 'constants.ArmCaptureTemplate' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepCaptureImage() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmLocation, "localhost")
|
|
||||||
stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName")
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
stateBag.Put(constants.ArmVirtualMachineCaptureParameters, &compute.VirtualMachineCaptureParameters{})
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmIsManagedImage, false)
|
|
||||||
stateBag.Put(constants.ArmManagedImageResourceGroupName, "")
|
|
||||||
stateBag.Put(constants.ArmManagedImageName, "")
|
|
||||||
stateBag.Put(constants.ArmImageParameters, &compute.Image{})
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepCertificateInKeyVault struct {
|
|
||||||
config *Config
|
|
||||||
client common.AZVaultClientIface
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepCertificateInKeyVault(cli common.AZVaultClientIface, ui packersdk.Ui, config *Config) *StepCertificateInKeyVault {
|
|
||||||
var step = &StepCertificateInKeyVault{
|
|
||||||
client: cli,
|
|
||||||
config: config,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
}
|
|
||||||
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCertificateInKeyVault) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
s.say("Setting the certificate in the KeyVault...")
|
|
||||||
var keyVaultName = state.Get(constants.ArmKeyVaultName).(string)
|
|
||||||
|
|
||||||
err := s.client.SetSecret(keyVaultName, DefaultSecretName, s.config.winrmCertificate)
|
|
||||||
if err != nil {
|
|
||||||
s.error(fmt.Errorf("Error setting winrm cert in custom keyvault: %s", err))
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepCertificateInKeyVault) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
azcommon "github.com/hashicorp/packer/builder/azure/common"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewStepCertificateInKeyVault(t *testing.T) {
|
|
||||||
cli := azcommon.MockAZVaultClient{}
|
|
||||||
ui := &packersdk.BasicUi{
|
|
||||||
Reader: new(bytes.Buffer),
|
|
||||||
Writer: new(bytes.Buffer),
|
|
||||||
}
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put(constants.ArmKeyVaultName, "testKeyVaultName")
|
|
||||||
|
|
||||||
config := &Config{
|
|
||||||
winrmCertificate: "testCertificateString",
|
|
||||||
}
|
|
||||||
|
|
||||||
certKVStep := NewStepCertificateInKeyVault(&cli, ui, config)
|
|
||||||
stepAction := certKVStep.Run(context.TODO(), state)
|
|
||||||
|
|
||||||
if stepAction == multistep.ActionHalt {
|
|
||||||
t.Fatalf("step should have succeeded.")
|
|
||||||
}
|
|
||||||
if !cli.SetSecretCalled {
|
|
||||||
t.Fatalf("Step should have called SetSecret on Azure client.")
|
|
||||||
}
|
|
||||||
if cli.SetSecretCert != "testCertificateString" {
|
|
||||||
t.Fatalf("Step should have read cert from winRMCertificate field on config.")
|
|
||||||
}
|
|
||||||
if cli.SetSecretVaultName != "testKeyVaultName" {
|
|
||||||
t.Fatalf("step should have read keyvault name from state.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewStepCertificateInKeyVault_error(t *testing.T) {
|
|
||||||
// Tell mock to return an error
|
|
||||||
cli := azcommon.MockAZVaultClient{}
|
|
||||||
cli.IsError = true
|
|
||||||
|
|
||||||
ui := &packersdk.BasicUi{
|
|
||||||
Reader: new(bytes.Buffer),
|
|
||||||
Writer: new(bytes.Buffer),
|
|
||||||
}
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put(constants.ArmKeyVaultName, "testKeyVaultName")
|
|
||||||
|
|
||||||
config := &Config{
|
|
||||||
winrmCertificate: "testCertificateString",
|
|
||||||
}
|
|
||||||
|
|
||||||
certKVStep := NewStepCertificateInKeyVault(&cli, ui, config)
|
|
||||||
stepAction := certKVStep.Run(context.TODO(), state)
|
|
||||||
|
|
||||||
if stepAction != multistep.ActionHalt {
|
|
||||||
t.Fatalf("step should have failed.")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,146 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepCreateResourceGroup struct {
|
|
||||||
client *AzureClient
|
|
||||||
create func(ctx context.Context, resourceGroupName string, location string, tags map[string]*string) error
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
exists func(ctx context.Context, resourceGroupName string) (bool, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepCreateResourceGroup(client *AzureClient, ui packersdk.Ui) *StepCreateResourceGroup {
|
|
||||||
var step = &StepCreateResourceGroup{
|
|
||||||
client: client,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
}
|
|
||||||
|
|
||||||
step.create = step.createResourceGroup
|
|
||||||
step.exists = step.doesResourceGroupExist
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCreateResourceGroup) createResourceGroup(ctx context.Context, resourceGroupName string, location string, tags map[string]*string) error {
|
|
||||||
_, err := s.client.GroupsClient.CreateOrUpdate(ctx, resourceGroupName, resources.Group{
|
|
||||||
Location: &location,
|
|
||||||
Tags: tags,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCreateResourceGroup) doesResourceGroupExist(ctx context.Context, resourceGroupName string) (bool, error) {
|
|
||||||
exists, err := s.client.GroupsClient.CheckExistence(ctx, resourceGroupName)
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return exists.Response.StatusCode != 404, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCreateResourceGroup) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
var doubleResource, ok = state.GetOk(constants.ArmDoubleResourceGroupNameSet)
|
|
||||||
if ok && doubleResource.(bool) {
|
|
||||||
err := errors.New("You have filled in both temp_resource_group_name and build_resource_group_name. Please choose one.")
|
|
||||||
return processStepResult(err, s.error, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
var location = state.Get(constants.ArmLocation).(string)
|
|
||||||
tags, ok := state.Get(constants.ArmTags).(map[string]*string)
|
|
||||||
if !ok {
|
|
||||||
err := fmt.Errorf("failed to extract tags from state bag")
|
|
||||||
state.Put(constants.Error, err)
|
|
||||||
s.error(err)
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
exists, err := s.exists(ctx, resourceGroupName)
|
|
||||||
if err != nil {
|
|
||||||
return processStepResult(err, s.error, state)
|
|
||||||
}
|
|
||||||
configThinksExists := state.Get(constants.ArmIsExistingResourceGroup).(bool)
|
|
||||||
if exists != configThinksExists {
|
|
||||||
if configThinksExists {
|
|
||||||
err = errors.New("The resource group you want to use does not exist yet. Please use temp_resource_group_name to create a temporary resource group.")
|
|
||||||
} else {
|
|
||||||
err = errors.New("A resource group with that name already exists. Please use build_resource_group_name to use an existing resource group.")
|
|
||||||
}
|
|
||||||
return processStepResult(err, s.error, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the resource group exists, we may not have permissions to update it so we don't.
|
|
||||||
if !exists {
|
|
||||||
s.say("Creating resource group ...")
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
|
||||||
s.say(fmt.Sprintf(" -> Location : '%s'", location))
|
|
||||||
s.say(fmt.Sprintf(" -> Tags :"))
|
|
||||||
for k, v := range tags {
|
|
||||||
s.say(fmt.Sprintf(" ->> %s : %s", k, *v))
|
|
||||||
}
|
|
||||||
err = s.create(ctx, resourceGroupName, location, tags)
|
|
||||||
if err == nil {
|
|
||||||
state.Put(constants.ArmIsResourceGroupCreated, true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s.say("Using existing resource group ...")
|
|
||||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
|
||||||
s.say(fmt.Sprintf(" -> Location : '%s'", location))
|
|
||||||
state.Put(constants.ArmIsResourceGroupCreated, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return processStepResult(err, s.error, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCreateResourceGroup) Cleanup(state multistep.StateBag) {
|
|
||||||
isCreated, ok := state.GetOk(constants.ArmIsResourceGroupCreated)
|
|
||||||
if !ok || !isCreated.(bool) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
if state.Get(constants.ArmIsExistingResourceGroup).(bool) {
|
|
||||||
ui.Say("\nThe resource group was not created by Packer, not deleting ...")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.TODO()
|
|
||||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
if exists, err := s.exists(ctx, resourceGroupName); !exists || err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Say("\nCleanup requested, deleting resource group ...")
|
|
||||||
f, err := s.client.GroupsClient.Delete(ctx, resourceGroupName)
|
|
||||||
if err == nil {
|
|
||||||
if state.Get(constants.ArmAsyncResourceGroupDelete).(bool) {
|
|
||||||
s.say(fmt.Sprintf("\n Not waiting for Resource Group delete as requested by user. Resource Group Name is %s", resourceGroupName))
|
|
||||||
} else {
|
|
||||||
err = f.WaitForCompletionRef(ctx, s.client.GroupsClient.Client)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf("Error deleting resource group. Please delete it manually.\n\n"+
|
|
||||||
"Name: %s\n"+
|
|
||||||
"Error: %s", resourceGroupName, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !state.Get(constants.ArmAsyncResourceGroupDelete).(bool) {
|
|
||||||
ui.Say("Resource group has been deleted.")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,251 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepCreateResourceGroupShouldFailIfBothGroupNames(t *testing.T) {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmDoubleResourceGroupNameSet, true)
|
|
||||||
|
|
||||||
value := "Unit Test: Tags"
|
|
||||||
tags := map[string]*string{
|
|
||||||
"tag01": &value,
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmTags, tags)
|
|
||||||
var testSubject = &StepCreateResourceGroup{
|
|
||||||
create: func(context.Context, string, string, map[string]*string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
exists: func(context.Context, string) (bool, error) { return false, nil },
|
|
||||||
}
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateResourceGroupShouldFailIfCreateFails(t *testing.T) {
|
|
||||||
var testSubject = &StepCreateResourceGroup{
|
|
||||||
create: func(context.Context, string, string, map[string]*string) error {
|
|
||||||
return fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
exists: func(context.Context, string) (bool, error) { return false, nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepCreateResourceGroup()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateResourceGroupShouldFailIfExistsFails(t *testing.T) {
|
|
||||||
var testSubject = &StepCreateResourceGroup{
|
|
||||||
create: func(context.Context, string, string, map[string]*string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
exists: func(context.Context, string) (bool, error) { return false, errors.New("FAIL") },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepCreateResourceGroup()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateResourceGroupShouldPassIfCreatePasses(t *testing.T) {
|
|
||||||
var testSubject = &StepCreateResourceGroup{
|
|
||||||
create: func(context.Context, string, string, map[string]*string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
exists: func(context.Context, string) (bool, error) { return false, nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepCreateResourceGroup()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateResourceGroupShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|
||||||
var actualResourceGroupName string
|
|
||||||
var actualLocation string
|
|
||||||
var actualTags map[string]*string
|
|
||||||
|
|
||||||
var testSubject = &StepCreateResourceGroup{
|
|
||||||
create: func(ctx context.Context, resourceGroupName string, location string, tags map[string]*string) error {
|
|
||||||
actualResourceGroupName = resourceGroupName
|
|
||||||
actualLocation = location
|
|
||||||
actualTags = tags
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
exists: func(context.Context, string) (bool, error) { return false, nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepCreateResourceGroup()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
var expectedLocation = stateBag.Get(constants.ArmLocation).(string)
|
|
||||||
var expectedTags = stateBag.Get(constants.ArmTags).(map[string]*string)
|
|
||||||
|
|
||||||
if actualResourceGroupName != expectedResourceGroupName {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualLocation != expectedLocation {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(expectedTags) != len(actualTags) && expectedTags["tag01"] != actualTags["tag01"] {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmTags' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, ok := stateBag.GetOk(constants.ArmIsResourceGroupCreated)
|
|
||||||
if !ok {
|
|
||||||
t.Fatal("Expected the step to add item to stateBag['constants.ArmIsResourceGroupCreated'], but it did not.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateResourceGroupMarkShouldFailIfTryingExistingButDoesntExist(t *testing.T) {
|
|
||||||
var testSubject = &StepCreateResourceGroup{
|
|
||||||
create: func(context.Context, string, string, map[string]*string) error {
|
|
||||||
return fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
exists: func(context.Context, string) (bool, error) { return false, nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestExistingStateBagStepCreateResourceGroup()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateResourceGroupMarkShouldFailIfTryingTempButExist(t *testing.T) {
|
|
||||||
var testSubject = &StepCreateResourceGroup{
|
|
||||||
create: func(context.Context, string, string, map[string]*string) error {
|
|
||||||
return fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
exists: func(context.Context, string) (bool, error) { return true, nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepCreateResourceGroup()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepCreateResourceGroup() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmLocation, "Unit Test: Location")
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, false)
|
|
||||||
|
|
||||||
value := "Unit Test: Tags"
|
|
||||||
tags := map[string]*string{
|
|
||||||
"tag01": &value,
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmTags, tags)
|
|
||||||
return stateBag
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestExistingStateBagStepCreateResourceGroup() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmLocation, "Unit Test: Location")
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
|
|
||||||
|
|
||||||
value := "Unit Test: Tags"
|
|
||||||
tags := map[string]*string{
|
|
||||||
"tag01": &value,
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmTags, tags)
|
|
||||||
return stateBag
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateResourceGroupShouldFailIfTagsFailCast(t *testing.T) {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmLocation, "Unit Test: Location")
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
|
|
||||||
|
|
||||||
value := "Unit Test: Tags"
|
|
||||||
tags := map[string]string{
|
|
||||||
"tag01": value,
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmTags, tags)
|
|
||||||
var testSubject = &StepCreateResourceGroup{
|
|
||||||
create: func(context.Context, string, string, map[string]*string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
exists: func(context.Context, string) (bool, error) { return false, nil },
|
|
||||||
}
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,118 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepDeleteAdditionalDisk struct {
|
|
||||||
client *AzureClient
|
|
||||||
delete func(string, string) error
|
|
||||||
deleteManaged func(context.Context, string, string) error
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepDeleteAdditionalDisks(client *AzureClient, ui packersdk.Ui) *StepDeleteAdditionalDisk {
|
|
||||||
var step = &StepDeleteAdditionalDisk{
|
|
||||||
client: client,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
}
|
|
||||||
|
|
||||||
step.delete = step.deleteBlob
|
|
||||||
step.deleteManaged = step.deleteManagedDisk
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeleteAdditionalDisk) deleteBlob(storageContainerName string, blobName string) error {
|
|
||||||
blob := s.client.BlobStorageClient.GetContainerReference(storageContainerName).GetBlobReference(blobName)
|
|
||||||
_, err := blob.BreakLease(nil)
|
|
||||||
if err != nil && !strings.Contains(err.Error(), "LeaseNotPresentWithLeaseOperation") {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = blob.Delete(nil)
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeleteAdditionalDisk) deleteManagedDisk(ctx context.Context, resourceGroupName string, diskName string) error {
|
|
||||||
xs := strings.Split(diskName, "/")
|
|
||||||
diskName = xs[len(xs)-1]
|
|
||||||
f, err := s.client.DisksClient.Delete(ctx, resourceGroupName, diskName)
|
|
||||||
if err == nil {
|
|
||||||
err = f.WaitForCompletionRef(ctx, s.client.DisksClient.Client)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeleteAdditionalDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
s.say("Deleting the temporary Additional disk ...")
|
|
||||||
|
|
||||||
var dataDisks []string
|
|
||||||
|
|
||||||
if disks := state.Get(constants.ArmAdditionalDiskVhds); disks != nil {
|
|
||||||
dataDisks = disks.([]string)
|
|
||||||
}
|
|
||||||
var isManagedDisk = state.Get(constants.ArmIsManagedImage).(bool)
|
|
||||||
var isExistingResourceGroup = state.Get(constants.ArmIsExistingResourceGroup).(bool)
|
|
||||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
|
|
||||||
if dataDisks == nil {
|
|
||||||
s.say(fmt.Sprintf(" -> No Additional Disks specified"))
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
if isManagedDisk && !isExistingResourceGroup {
|
|
||||||
s.say(fmt.Sprintf(" -> Additional Disk : skipping, managed disk was used..."))
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, additionaldisk := range dataDisks {
|
|
||||||
s.say(fmt.Sprintf(" -> Additional Disk %d: '%s'", i+1, additionaldisk))
|
|
||||||
var err error
|
|
||||||
if isManagedDisk {
|
|
||||||
err = s.deleteManaged(ctx, resourceGroupName, additionaldisk)
|
|
||||||
if err != nil {
|
|
||||||
s.say("Failed to delete the managed Additional Disk!")
|
|
||||||
return processStepResult(err, s.error, state)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := url.Parse(additionaldisk)
|
|
||||||
if err != nil {
|
|
||||||
s.say("Failed to parse the Additional Disk's VHD URI!")
|
|
||||||
return processStepResult(err, s.error, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
xs := strings.Split(u.Path, "/")
|
|
||||||
if len(xs) < 3 {
|
|
||||||
err = errors.New("Failed to parse Additional Disk's VHD URI!")
|
|
||||||
} else {
|
|
||||||
var storageAccountName = xs[1]
|
|
||||||
var blobName = strings.Join(xs[2:], "/")
|
|
||||||
|
|
||||||
err = s.delete(storageAccountName, blobName)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return processStepResult(err, s.error, state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepDeleteAdditionalDisk) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,227 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepDeleteAdditionalDiskShouldFailIfGetFails(t *testing.T) {
|
|
||||||
var testSubject = &StepDeleteAdditionalDisk{
|
|
||||||
delete: func(string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
|
|
||||||
deleteManaged: func(context.Context, string, string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/images/pkrvm_os.vhd"})
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeleteAdditionalDiskShouldPassIfGetPasses(t *testing.T) {
|
|
||||||
var testSubject = &StepDeleteAdditionalDisk{
|
|
||||||
delete: func(string, string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/images/pkrvm_os.vhd"})
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeleteAdditionalDiskShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|
||||||
var actualStorageContainerName string
|
|
||||||
var actualBlobName string
|
|
||||||
|
|
||||||
var testSubject = &StepDeleteAdditionalDisk{
|
|
||||||
delete: func(storageContainerName string, blobName string) error {
|
|
||||||
actualStorageContainerName = storageContainerName
|
|
||||||
actualBlobName = blobName
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/images/pkrvm_os.vhd"})
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualStorageContainerName != "images" {
|
|
||||||
t.Fatalf("Expected the storage container name to be 'images', but found '%s'.", actualStorageContainerName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualBlobName != "pkrvm_os.vhd" {
|
|
||||||
t.Fatalf("Expected the blob name to be 'pkrvm_os.vhd', but found '%s'.", actualBlobName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeleteAdditionalDiskShouldHandleComplexStorageContainerNames(t *testing.T) {
|
|
||||||
var actualStorageContainerName string
|
|
||||||
var actualBlobName string
|
|
||||||
|
|
||||||
var testSubject = &StepDeleteAdditionalDisk{
|
|
||||||
delete: func(storageContainerName string, blobName string) error {
|
|
||||||
actualStorageContainerName = storageContainerName
|
|
||||||
actualBlobName = blobName
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://storage.blob.core.windows.net/abc/def/pkrvm_os.vhd"})
|
|
||||||
testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if actualStorageContainerName != "abc" {
|
|
||||||
t.Fatalf("Expected the storage container name to be 'abc/def', but found '%s'.", actualStorageContainerName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualBlobName != "def/pkrvm_os.vhd" {
|
|
||||||
t.Fatalf("Expected the blob name to be 'pkrvm_os.vhd', but found '%s'.", actualBlobName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeleteAdditionalDiskShouldFailIfVHDNameCannotBeURLParsed(t *testing.T) {
|
|
||||||
var testSubject = &StepDeleteAdditionalDisk{
|
|
||||||
delete: func(string, string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
deleteManaged: func(context.Context, string, string) error { return nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid URL per https://golang.org/src/net/url/url_test.go
|
|
||||||
stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"http://[fe80::1%en0]/"})
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%v'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to not stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestStepDeleteAdditionalDiskShouldFailIfVHDNameIsTooShort(t *testing.T) {
|
|
||||||
var testSubject = &StepDeleteAdditionalDisk{
|
|
||||||
delete: func(string, string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
deleteManaged: func(context.Context, string, string) error { return nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := DeleteTestStateBagStepDeleteAdditionalDisk([]string{"storage.blob.core.windows.net/abc"})
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to not stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeleteAdditionalDiskShouldPassIfManagedDiskInTempResourceGroup(t *testing.T) {
|
|
||||||
var testSubject = &StepDeleteAdditionalDisk{
|
|
||||||
delete: func(string, string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"})
|
|
||||||
stateBag.Put(constants.ArmIsManagedImage, true)
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, false)
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "testgroup")
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeleteAdditionalDiskShouldFailIfManagedDiskInExistingResourceGroupFailsToDelete(t *testing.T) {
|
|
||||||
var testSubject = &StepDeleteAdditionalDisk{
|
|
||||||
delete: func(string, string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
deleteManaged: func(context.Context, string, string) error { return errors.New("UNIT TEST FAIL!") },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"})
|
|
||||||
stateBag.Put(constants.ArmIsManagedImage, true)
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "testgroup")
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to not stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeleteAdditionalDiskShouldFailIfManagedDiskInExistingResourceGroupIsDeleted(t *testing.T) {
|
|
||||||
var testSubject = &StepDeleteAdditionalDisk{
|
|
||||||
delete: func(string, string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
deleteManaged: func(context.Context, string, string) error { return nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"})
|
|
||||||
stateBag.Put(constants.ArmIsManagedImage, true)
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "testgroup")
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteTestStateBagStepDeleteAdditionalDisk(osDiskVhds []string) multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
stateBag.Put(constants.ArmAdditionalDiskVhds, osDiskVhds)
|
|
||||||
stateBag.Put(constants.ArmIsManagedImage, false)
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, false)
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "testgroup")
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
|
@ -1,306 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/retry"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepDeployTemplate struct {
|
|
||||||
client *AzureClient
|
|
||||||
deploy func(ctx context.Context, resourceGroupName string, deploymentName string) error
|
|
||||||
delete func(ctx context.Context, deploymentName, resourceGroupName string) error
|
|
||||||
disk func(ctx context.Context, resourceGroupName string, computeName string) (string, string, error)
|
|
||||||
deleteDisk func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error
|
|
||||||
deleteDeployment func(ctx context.Context, state multistep.StateBag) error
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
config *Config
|
|
||||||
factory templateFactoryFunc
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepDeployTemplate(client *AzureClient, ui packersdk.Ui, config *Config, deploymentName string, factory templateFactoryFunc) *StepDeployTemplate {
|
|
||||||
var step = &StepDeployTemplate{
|
|
||||||
client: client,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
config: config,
|
|
||||||
factory: factory,
|
|
||||||
name: deploymentName,
|
|
||||||
}
|
|
||||||
|
|
||||||
step.deploy = step.deployTemplate
|
|
||||||
step.delete = step.deleteDeploymentResources
|
|
||||||
step.disk = step.getImageDetails
|
|
||||||
step.deleteDisk = step.deleteImage
|
|
||||||
step.deleteDeployment = step.deleteDeploymentObject
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
s.say("Deploying deployment template ...")
|
|
||||||
|
|
||||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
|
||||||
s.say(fmt.Sprintf(" -> DeploymentName : '%s'", s.name))
|
|
||||||
|
|
||||||
return processStepResult(
|
|
||||||
s.deploy(ctx, resourceGroupName, s.name),
|
|
||||||
s.error, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
|
|
||||||
defer func() {
|
|
||||||
err := s.deleteDeployment(context.Background(), state)
|
|
||||||
if err != nil {
|
|
||||||
s.say(err.Error())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
ui.Say("\nDeleting individual resources ...")
|
|
||||||
|
|
||||||
deploymentName := s.name
|
|
||||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
// Get image disk details before deleting the image; otherwise we won't be able to
|
|
||||||
// delete the disk as the image request will return a 404
|
|
||||||
computeName := state.Get(constants.ArmComputeName).(string)
|
|
||||||
imageType, imageName, err := s.disk(context.TODO(), resourceGroupName, computeName)
|
|
||||||
|
|
||||||
if err != nil && !strings.Contains(err.Error(), "ResourceNotFound") {
|
|
||||||
ui.Error(fmt.Sprintf("Could not retrieve OS Image details: %s", err))
|
|
||||||
}
|
|
||||||
err = s.delete(context.TODO(), deploymentName, resourceGroupName)
|
|
||||||
if err != nil {
|
|
||||||
s.reportIfError(err, resourceGroupName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The disk was not found on the VM, this is an error.
|
|
||||||
if imageType == "" && imageName == "" {
|
|
||||||
ui.Error(fmt.Sprintf("Failed to find temporary OS disk on VM. Please delete manually.\n\n"+
|
|
||||||
"VM Name: %s\n"+
|
|
||||||
"Error: %s", computeName, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !state.Get(constants.ArmKeepOSDisk).(bool) {
|
|
||||||
ui.Say(fmt.Sprintf(" Deleting -> %s : '%s'", imageType, imageName))
|
|
||||||
err = s.deleteDisk(context.TODO(), imageType, imageName, resourceGroupName)
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
|
||||||
"Name: %s\n"+
|
|
||||||
"Error: %s", imageName, err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error {
|
|
||||||
deployment, err := s.factory(s.config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := s.client.DeploymentsClient.CreateOrUpdate(ctx, resourceGroupName, deploymentName, *deployment)
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
|
||||||
if err == nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeployTemplate) deleteDeploymentObject(ctx context.Context, state multistep.StateBag) error {
|
|
||||||
deploymentName := s.name
|
|
||||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Removing the created Deployment object: '%s'", deploymentName))
|
|
||||||
f, err := s.client.DeploymentsClient.Delete(ctx, resourceGroupName, deploymentName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.WaitForCompletionRef(ctx, s.client.DeploymentsClient.Client)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeployTemplate) getImageDetails(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) {
|
|
||||||
//We can't depend on constants.ArmOSDiskVhd being set
|
|
||||||
var imageName, imageType string
|
|
||||||
vm, err := s.client.VirtualMachinesClient.Get(ctx, resourceGroupName, computeName, "")
|
|
||||||
if err != nil {
|
|
||||||
return imageName, imageType, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if vm.StorageProfile.OsDisk.Vhd != nil {
|
|
||||||
imageType = "image"
|
|
||||||
imageName = *vm.StorageProfile.OsDisk.Vhd.URI
|
|
||||||
return imageType, imageName, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
imageType = "Microsoft.Compute/disks"
|
|
||||||
imageName = *vm.StorageProfile.OsDisk.ManagedDisk.ID
|
|
||||||
|
|
||||||
return imageType, imageName, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO(paulmey): move to helpers file
|
|
||||||
func deleteResource(ctx context.Context, client *AzureClient, resourceType string, resourceName string, resourceGroupName string) error {
|
|
||||||
switch resourceType {
|
|
||||||
case "Microsoft.Compute/virtualMachines":
|
|
||||||
f, err := client.VirtualMachinesClient.Delete(ctx, resourceGroupName, resourceName)
|
|
||||||
if err == nil {
|
|
||||||
err = f.WaitForCompletionRef(ctx, client.VirtualMachinesClient.Client)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
case "Microsoft.KeyVault/vaults":
|
|
||||||
_, err := client.VaultClientDelete.Delete(ctx, resourceGroupName, resourceName)
|
|
||||||
return err
|
|
||||||
case "Microsoft.Network/networkInterfaces":
|
|
||||||
f, err := client.InterfacesClient.Delete(ctx, resourceGroupName, resourceName)
|
|
||||||
if err == nil {
|
|
||||||
err = f.WaitForCompletionRef(ctx, client.InterfacesClient.Client)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
case "Microsoft.Network/virtualNetworks":
|
|
||||||
f, err := client.VirtualNetworksClient.Delete(ctx, resourceGroupName, resourceName)
|
|
||||||
if err == nil {
|
|
||||||
err = f.WaitForCompletionRef(ctx, client.VirtualNetworksClient.Client)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
case "Microsoft.Network/networkSecurityGroups":
|
|
||||||
f, err := client.SecurityGroupsClient.Delete(ctx, resourceGroupName, resourceName)
|
|
||||||
if err == nil {
|
|
||||||
err = f.WaitForCompletionRef(ctx, client.SecurityGroupsClient.Client)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
case "Microsoft.Network/publicIPAddresses":
|
|
||||||
f, err := client.PublicIPAddressesClient.Delete(ctx, resourceGroupName, resourceName)
|
|
||||||
if err == nil {
|
|
||||||
err = f.WaitForCompletionRef(ctx, client.PublicIPAddressesClient.Client)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeployTemplate) deleteImage(ctx context.Context, imageType string, imageName string, resourceGroupName string) error {
|
|
||||||
// Managed disk
|
|
||||||
if imageType == "Microsoft.Compute/disks" {
|
|
||||||
xs := strings.Split(imageName, "/")
|
|
||||||
diskName := xs[len(xs)-1]
|
|
||||||
f, err := s.client.DisksClient.Delete(ctx, resourceGroupName, diskName)
|
|
||||||
if err == nil {
|
|
||||||
err = f.WaitForCompletionRef(ctx, s.client.DisksClient.Client)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VHD image
|
|
||||||
u, err := url.Parse(imageName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
xs := strings.Split(u.Path, "/")
|
|
||||||
if len(xs) < 3 {
|
|
||||||
return errors.New("Unable to parse path of image " + imageName)
|
|
||||||
}
|
|
||||||
var storageAccountName = xs[1]
|
|
||||||
var blobName = strings.Join(xs[2:], "/")
|
|
||||||
|
|
||||||
blob := s.client.BlobStorageClient.GetContainerReference(storageAccountName).GetBlobReference(blobName)
|
|
||||||
_, err = blob.BreakLease(nil)
|
|
||||||
if err != nil && !strings.Contains(err.Error(), "LeaseNotPresentWithLeaseOperation") {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return blob.Delete(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, deploymentName, resourceGroupName string) error {
|
|
||||||
var maxResources int32 = 50
|
|
||||||
deploymentOperations, err := s.client.DeploymentOperationsClient.ListComplete(ctx, resourceGroupName, deploymentName, &maxResources)
|
|
||||||
if err != nil {
|
|
||||||
s.reportIfError(err, resourceGroupName)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
resources := map[string]string{}
|
|
||||||
|
|
||||||
for deploymentOperations.NotDone() {
|
|
||||||
deploymentOperation := deploymentOperations.Value()
|
|
||||||
// Sometimes an empty operation is added to the list by Azure
|
|
||||||
if deploymentOperation.Properties.TargetResource == nil {
|
|
||||||
_ = deploymentOperations.Next()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceName := *deploymentOperation.Properties.TargetResource.ResourceName
|
|
||||||
resourceType := *deploymentOperation.Properties.TargetResource.ResourceType
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf("Adding to deletion queue -> %s : '%s'", resourceType, resourceName))
|
|
||||||
resources[resourceType] = resourceName
|
|
||||||
|
|
||||||
if err = deploymentOperations.Next(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(len(resources))
|
|
||||||
|
|
||||||
for resourceType, resourceName := range resources {
|
|
||||||
go func(resourceType, resourceName string) {
|
|
||||||
defer wg.Done()
|
|
||||||
retryConfig := retry.Config{
|
|
||||||
Tries: 10,
|
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 600 * time.Second, Multiplier: 2}).Linear,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = retryConfig.Run(ctx, func(ctx context.Context) error {
|
|
||||||
s.say(fmt.Sprintf("Attempting deletion -> %s : '%s'", resourceType, resourceName))
|
|
||||||
err := deleteResource(ctx, s.client,
|
|
||||||
resourceType,
|
|
||||||
resourceName,
|
|
||||||
resourceGroupName)
|
|
||||||
if err != nil {
|
|
||||||
s.say(fmt.Sprintf("Error deleting resource. Will retry.\n"+
|
|
||||||
"Name: %s\n"+
|
|
||||||
"Error: %s\n", resourceName, err.Error()))
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
s.reportIfError(err, resourceName)
|
|
||||||
}
|
|
||||||
}(resourceType, resourceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.say("Waiting for deletion of all resources...")
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepDeployTemplate) reportIfError(err error, resourceName string) {
|
|
||||||
if err != nil {
|
|
||||||
s.say(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
|
||||||
"Name: %s\n"+
|
|
||||||
"Error: %s", resourceName, err.Error()))
|
|
||||||
s.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,209 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepDeployTemplateShouldFailIfDeployFails(t *testing.T) {
|
|
||||||
var testSubject = &StepDeployTemplate{
|
|
||||||
deploy: func(context.Context, string, string) error {
|
|
||||||
return fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepDeployTemplate()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeployTemplateShouldPassIfDeployPasses(t *testing.T) {
|
|
||||||
var testSubject = &StepDeployTemplate{
|
|
||||||
deploy: func(context.Context, string, string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepDeployTemplate()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|
||||||
var actualResourceGroupName string
|
|
||||||
var actualDeploymentName string
|
|
||||||
|
|
||||||
var testSubject = &StepDeployTemplate{
|
|
||||||
deploy: func(ctx context.Context, resourceGroupName string, deploymentName string) error {
|
|
||||||
actualResourceGroupName = resourceGroupName
|
|
||||||
actualDeploymentName = deploymentName
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
name: "--deployment-name--",
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepValidateTemplate()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
|
|
||||||
if actualDeploymentName != "--deployment-name--" {
|
|
||||||
t.Fatal("Expected StepValidateTemplate to source 'constants.ArmDeploymentName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualResourceGroupName != expectedResourceGroupName {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeployTemplateDeleteImageShouldFailWhenImageUrlCannotBeParsed(t *testing.T) {
|
|
||||||
var testSubject = &StepDeployTemplate{
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
name: "--deployment-name--",
|
|
||||||
}
|
|
||||||
// Invalid URL per https://golang.org/src/net/url/url_test.go
|
|
||||||
err := testSubject.deleteImage(context.TODO(), "image", "http://[fe80::1%en0]/", "Unit Test: ResourceGroupName")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Expected a failure because of the failed image name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeployTemplateDeleteImageShouldFailWithInvalidImage(t *testing.T) {
|
|
||||||
var testSubject = &StepDeployTemplate{
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
name: "--deployment-name--",
|
|
||||||
}
|
|
||||||
err := testSubject.deleteImage(context.TODO(), "image", "storage.blob.core.windows.net/abc", "Unit Test: ResourceGroupName")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Expected a failure because of the failed image name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeployTemplateCleanupShouldDeleteManagedOSImageInExistingResourceGroup(t *testing.T) {
|
|
||||||
var deleteDiskCounter = 0
|
|
||||||
var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter)
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepDeployTemplate()
|
|
||||||
stateBag.Put(constants.ArmIsManagedImage, true)
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
|
|
||||||
stateBag.Put(constants.ArmIsResourceGroupCreated, true)
|
|
||||||
stateBag.Put(constants.ArmKeepOSDisk, false)
|
|
||||||
stateBag.Put("ui", packersdk.TestUi(t))
|
|
||||||
|
|
||||||
testSubject.Cleanup(stateBag)
|
|
||||||
if deleteDiskCounter != 1 {
|
|
||||||
t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 time, but invoked %d times", deleteDiskCounter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeployTemplateCleanupShouldDeleteManagedOSImageInTemporaryResourceGroup(t *testing.T) {
|
|
||||||
var deleteDiskCounter = 0
|
|
||||||
var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter)
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepDeployTemplate()
|
|
||||||
stateBag.Put(constants.ArmIsManagedImage, true)
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, false)
|
|
||||||
stateBag.Put(constants.ArmIsResourceGroupCreated, true)
|
|
||||||
stateBag.Put(constants.ArmKeepOSDisk, false)
|
|
||||||
stateBag.Put("ui", packersdk.TestUi(t))
|
|
||||||
|
|
||||||
testSubject.Cleanup(stateBag)
|
|
||||||
if deleteDiskCounter != 1 {
|
|
||||||
t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 times, but invoked %d times", deleteDiskCounter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeployTemplateCleanupShouldDeleteVHDOSImageInExistingResourceGroup(t *testing.T) {
|
|
||||||
var deleteDiskCounter = 0
|
|
||||||
var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter)
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepDeployTemplate()
|
|
||||||
stateBag.Put(constants.ArmIsManagedImage, false)
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
|
|
||||||
stateBag.Put(constants.ArmIsResourceGroupCreated, true)
|
|
||||||
stateBag.Put(constants.ArmKeepOSDisk, false)
|
|
||||||
stateBag.Put("ui", packersdk.TestUi(t))
|
|
||||||
|
|
||||||
testSubject.Cleanup(stateBag)
|
|
||||||
if deleteDiskCounter != 1 {
|
|
||||||
t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 time, but invoked %d times", deleteDiskCounter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepDeployTemplateCleanupShouldVHDOSImageInTemporaryResourceGroup(t *testing.T) {
|
|
||||||
var deleteDiskCounter = 0
|
|
||||||
var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter)
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepDeployTemplate()
|
|
||||||
stateBag.Put(constants.ArmIsManagedImage, false)
|
|
||||||
stateBag.Put(constants.ArmIsExistingResourceGroup, false)
|
|
||||||
stateBag.Put(constants.ArmIsResourceGroupCreated, true)
|
|
||||||
stateBag.Put(constants.ArmKeepOSDisk, false)
|
|
||||||
stateBag.Put("ui", packersdk.TestUi(t))
|
|
||||||
|
|
||||||
testSubject.Cleanup(stateBag)
|
|
||||||
if deleteDiskCounter != 1 {
|
|
||||||
t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 times, but invoked %d times", deleteDiskCounter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepDeployTemplate() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName")
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName")
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStepDeployTemplateDeleteOSImage(deleteDiskCounter *int) *StepDeployTemplate {
|
|
||||||
return &StepDeployTemplate{
|
|
||||||
deploy: func(context.Context, string, string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
deleteDisk: func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error {
|
|
||||||
*deleteDiskCounter++
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
disk: func(ctx context.Context, resourceGroupName, computeName string) (string, string, error) {
|
|
||||||
return "Microsoft.Compute/disks", "", nil
|
|
||||||
},
|
|
||||||
delete: func(ctx context.Context, deploymentName, resourceGroupName string) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
deleteDeployment: func(ctx context.Context, state multistep.StateBag) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepGetDataDisk struct {
|
|
||||||
client *AzureClient
|
|
||||||
query func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error)
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepGetAdditionalDisks(client *AzureClient, ui packersdk.Ui) *StepGetDataDisk {
|
|
||||||
var step = &StepGetDataDisk{
|
|
||||||
client: client,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
}
|
|
||||||
|
|
||||||
step.query = step.queryCompute
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepGetDataDisk) queryCompute(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) {
|
|
||||||
vm, err := s.client.VirtualMachinesClient.Get(ctx, resourceGroupName, computeName, "")
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
return vm, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepGetDataDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
s.say("Querying the machine's additional disks properties ...")
|
|
||||||
|
|
||||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
var computeName = state.Get(constants.ArmComputeName).(string)
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
|
||||||
s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName))
|
|
||||||
|
|
||||||
vm, err := s.query(ctx, resourceGroupName, computeName)
|
|
||||||
if err != nil {
|
|
||||||
state.Put(constants.Error, err)
|
|
||||||
s.error(err)
|
|
||||||
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
if vm.StorageProfile.DataDisks != nil {
|
|
||||||
var vhdUri string
|
|
||||||
additional_disks := make([]string, len(*vm.StorageProfile.DataDisks))
|
|
||||||
for i, additionaldisk := range *vm.StorageProfile.DataDisks {
|
|
||||||
if additionaldisk.Vhd != nil {
|
|
||||||
vhdUri = *additionaldisk.Vhd.URI
|
|
||||||
s.say(fmt.Sprintf(" -> Additional Disk %d : '%s'", i+1, vhdUri))
|
|
||||||
} else {
|
|
||||||
vhdUri = *additionaldisk.ManagedDisk.ID
|
|
||||||
s.say(fmt.Sprintf(" -> Managed Additional Disk %d : '%s'", i+1, vhdUri))
|
|
||||||
}
|
|
||||||
additional_disks[i] = vhdUri
|
|
||||||
}
|
|
||||||
state.Put(constants.ArmAdditionalDiskVhds, additional_disks)
|
|
||||||
}
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepGetDataDisk) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepGetAdditionalDiskShouldFailIfGetFails(t *testing.T) {
|
|
||||||
var testSubject = &StepGetDataDisk{
|
|
||||||
query: func(context.Context, string, string) (compute.VirtualMachine, error) {
|
|
||||||
return createVirtualMachineWithDataDisksFromUri("test.vhd"), fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetAdditionalDisks()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepGetAdditionalDiskShouldPassIfGetPasses(t *testing.T) {
|
|
||||||
var testSubject = &StepGetDataDisk{
|
|
||||||
query: func(context.Context, string, string) (compute.VirtualMachine, error) {
|
|
||||||
return createVirtualMachineWithDataDisksFromUri("test.vhd"), nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetAdditionalDisks()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepGetAdditionalDiskShouldTakeValidateArgumentsFromStateBag(t *testing.T) {
|
|
||||||
var actualResourceGroupName string
|
|
||||||
var actualComputeName string
|
|
||||||
|
|
||||||
var testSubject = &StepGetDataDisk{
|
|
||||||
query: func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) {
|
|
||||||
actualResourceGroupName = resourceGroupName
|
|
||||||
actualComputeName = computeName
|
|
||||||
|
|
||||||
return createVirtualMachineWithDataDisksFromUri("test.vhd"), nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetAdditionalDisks()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string)
|
|
||||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
|
|
||||||
if actualComputeName != expectedComputeName {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualResourceGroupName != expectedResourceGroupName {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedAdditionalDiskVhds, ok := stateBag.GetOk(constants.ArmAdditionalDiskVhds)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.ArmAdditionalDiskVhds)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedAdditionalDiskVhd := expectedAdditionalDiskVhds.([]string)
|
|
||||||
if expectedAdditionalDiskVhd[0] != "test.vhd" {
|
|
||||||
t.Fatalf("Expected the value of stateBag[%s] to be 'test.vhd', but got '%s'.", constants.ArmAdditionalDiskVhds, expectedAdditionalDiskVhd[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepGetAdditionalDisks() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName")
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
||||||
|
|
||||||
func createVirtualMachineWithDataDisksFromUri(vhdUri string) compute.VirtualMachine {
|
|
||||||
vm := compute.VirtualMachine{
|
|
||||||
VirtualMachineProperties: &compute.VirtualMachineProperties{
|
|
||||||
StorageProfile: &compute.StorageProfile{
|
|
||||||
OsDisk: &compute.OSDisk{
|
|
||||||
Vhd: &compute.VirtualHardDisk{
|
|
||||||
URI: &vhdUri,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
DataDisks: &[]compute.DataDisk{
|
|
||||||
{
|
|
||||||
Vhd: &compute.VirtualHardDisk{
|
|
||||||
URI: &vhdUri,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return vm
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepGetCertificate struct {
|
|
||||||
client *AzureClient
|
|
||||||
get func(keyVaultName string, secretName string) (string, error)
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
pause func()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepGetCertificate(client *AzureClient, ui packersdk.Ui) *StepGetCertificate {
|
|
||||||
var step = &StepGetCertificate{
|
|
||||||
client: client,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
pause: func() { time.Sleep(30 * time.Second) },
|
|
||||||
}
|
|
||||||
|
|
||||||
step.get = step.getCertificateUrl
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepGetCertificate) getCertificateUrl(keyVaultName string, secretName string) (string, error) {
|
|
||||||
secret, err := s.client.GetSecret(keyVaultName, secretName)
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return *secret.ID, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepGetCertificate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
s.say("Getting the certificate's URL ...")
|
|
||||||
|
|
||||||
var keyVaultName = state.Get(constants.ArmKeyVaultName).(string)
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> Key Vault Name : '%s'", keyVaultName))
|
|
||||||
s.say(fmt.Sprintf(" -> Key Vault Secret Name : '%s'", DefaultSecretName))
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var url string
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
url, err = s.get(keyVaultName, DefaultSecretName)
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" ...failed to get certificate URL, retry(%d)", i))
|
|
||||||
s.pause()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
state.Put(constants.Error, err)
|
|
||||||
s.error(err)
|
|
||||||
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> Certificate URL : '%s'", url))
|
|
||||||
state.Put(constants.ArmCertificateUrl, url)
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepGetCertificate) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepGetCertificateShouldFailIfGetFails(t *testing.T) {
|
|
||||||
var testSubject = &StepGetCertificate{
|
|
||||||
get: func(string, string) (string, error) { return "", fmt.Errorf("!! Unit Test FAIL !!") },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
pause: func() {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetCertificate()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepGetCertificateShouldPassIfGetPasses(t *testing.T) {
|
|
||||||
var testSubject = &StepGetCertificate{
|
|
||||||
get: func(string, string) (string, error) { return "", nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
pause: func() {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetCertificate()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepGetCertificateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|
||||||
var actualKeyVaultName string
|
|
||||||
var actualSecretName string
|
|
||||||
|
|
||||||
var testSubject = &StepGetCertificate{
|
|
||||||
get: func(keyVaultName string, secretName string) (string, error) {
|
|
||||||
actualKeyVaultName = keyVaultName
|
|
||||||
actualSecretName = secretName
|
|
||||||
|
|
||||||
return "http://key.vault/1", nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
pause: func() {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetCertificate()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedKeyVaultName = stateBag.Get(constants.ArmKeyVaultName).(string)
|
|
||||||
|
|
||||||
if actualKeyVaultName != expectedKeyVaultName {
|
|
||||||
t.Fatal("Expected StepGetCertificate to source 'constants.ArmKeyVaultName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
if actualSecretName != DefaultSecretName {
|
|
||||||
t.Fatal("Expected StepGetCertificate to use default value for secret, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedCertificateUrl, ok := stateBag.GetOk(constants.ArmCertificateUrl)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.ArmCertificateUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
if expectedCertificateUrl != "http://key.vault/1" {
|
|
||||||
t.Fatalf("Expected the value of stateBag[%s] to be 'http://key.vault/1', but got '%s'.", constants.ArmCertificateUrl, expectedCertificateUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepGetCertificate() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
stateBag.Put(constants.ArmKeyVaultName, "Unit Test: KeyVaultName")
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type EndpointType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
PublicEndpoint EndpointType = iota
|
|
||||||
PrivateEndpoint
|
|
||||||
PublicEndpointInPrivateNetwork
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
EndpointCommunicationText = map[EndpointType]string{
|
|
||||||
PublicEndpoint: "PublicEndpoint",
|
|
||||||
PrivateEndpoint: "PrivateEndpoint",
|
|
||||||
PublicEndpointInPrivateNetwork: "PublicEndpointInPrivateNetwork",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepGetIPAddress struct {
|
|
||||||
client *AzureClient
|
|
||||||
endpoint EndpointType
|
|
||||||
get func(ctx context.Context, resourceGroupName string, ipAddressName string, interfaceName string) (string, error)
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepGetIPAddress(client *AzureClient, ui packersdk.Ui, endpoint EndpointType) *StepGetIPAddress {
|
|
||||||
var step = &StepGetIPAddress{
|
|
||||||
client: client,
|
|
||||||
endpoint: endpoint,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
}
|
|
||||||
|
|
||||||
switch endpoint {
|
|
||||||
case PrivateEndpoint:
|
|
||||||
step.get = step.getPrivateIP
|
|
||||||
case PublicEndpoint:
|
|
||||||
step.get = step.getPublicIP
|
|
||||||
case PublicEndpointInPrivateNetwork:
|
|
||||||
step.get = step.getPublicIPInPrivateNetwork
|
|
||||||
}
|
|
||||||
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepGetIPAddress) getPrivateIP(ctx context.Context, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) {
|
|
||||||
resp, err := s.client.InterfacesClient.Get(ctx, resourceGroupName, interfaceName, "")
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return *(*resp.IPConfigurations)[0].PrivateIPAddress, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepGetIPAddress) getPublicIP(ctx context.Context, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) {
|
|
||||||
resp, err := s.client.PublicIPAddressesClient.Get(ctx, resourceGroupName, ipAddressName, "")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return *resp.IPAddress, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepGetIPAddress) getPublicIPInPrivateNetwork(ctx context.Context, resourceGroupName string, ipAddressName string, interfaceName string) (string, error) {
|
|
||||||
s.getPrivateIP(ctx, resourceGroupName, ipAddressName, interfaceName)
|
|
||||||
return s.getPublicIP(ctx, resourceGroupName, ipAddressName, interfaceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepGetIPAddress) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
s.say("Getting the VM's IP address ...")
|
|
||||||
|
|
||||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
var ipAddressName = state.Get(constants.ArmPublicIPAddressName).(string)
|
|
||||||
var nicName = state.Get(constants.ArmNicName).(string)
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
|
||||||
s.say(fmt.Sprintf(" -> PublicIPAddressName : '%s'", ipAddressName))
|
|
||||||
s.say(fmt.Sprintf(" -> NicName : '%s'", nicName))
|
|
||||||
s.say(fmt.Sprintf(" -> Network Connection : '%s'", EndpointCommunicationText[s.endpoint]))
|
|
||||||
|
|
||||||
address, err := s.get(ctx, resourceGroupName, ipAddressName, nicName)
|
|
||||||
if err != nil {
|
|
||||||
state.Put(constants.Error, err)
|
|
||||||
s.error(err)
|
|
||||||
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
state.Put(constants.SSHHost, address)
|
|
||||||
s.say(fmt.Sprintf(" -> IP Address : '%s'", address))
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepGetIPAddress) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,124 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepGetIPAddressShouldFailIfGetFails(t *testing.T) {
|
|
||||||
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
|
|
||||||
|
|
||||||
for _, endpoint := range endpoints {
|
|
||||||
var testSubject = &StepGetIPAddress{
|
|
||||||
get: func(context.Context, string, string, string) (string, error) {
|
|
||||||
return "", fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
endpoint: endpoint,
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetIPAddress()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepGetIPAddressShouldPassIfGetPasses(t *testing.T) {
|
|
||||||
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
|
|
||||||
|
|
||||||
for _, endpoint := range endpoints {
|
|
||||||
var testSubject = &StepGetIPAddress{
|
|
||||||
get: func(context.Context, string, string, string) (string, error) { return "", nil },
|
|
||||||
endpoint: endpoint,
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetIPAddress()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepGetIPAddressShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|
||||||
var actualResourceGroupName string
|
|
||||||
var actualIPAddressName string
|
|
||||||
var actualNicName string
|
|
||||||
endpoints := []EndpointType{PublicEndpoint, PublicEndpointInPrivateNetwork}
|
|
||||||
|
|
||||||
for _, endpoint := range endpoints {
|
|
||||||
var testSubject = &StepGetIPAddress{
|
|
||||||
get: func(ctx context.Context, resourceGroupName string, ipAddressName string, nicName string) (string, error) {
|
|
||||||
actualResourceGroupName = resourceGroupName
|
|
||||||
actualIPAddressName = ipAddressName
|
|
||||||
actualNicName = nicName
|
|
||||||
|
|
||||||
return "127.0.0.1", nil
|
|
||||||
},
|
|
||||||
endpoint: endpoint,
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetIPAddress()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
var expectedIPAddressName = stateBag.Get(constants.ArmPublicIPAddressName).(string)
|
|
||||||
var expectedNicName = stateBag.Get(constants.ArmNicName).(string)
|
|
||||||
|
|
||||||
if actualIPAddressName != expectedIPAddressName {
|
|
||||||
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmIPAddressName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualResourceGroupName != expectedResourceGroupName {
|
|
||||||
t.Fatal("Expected StepGetIPAddress to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualNicName != expectedNicName {
|
|
||||||
t.Fatalf("Expected StepGetIPAddress to source 'constants.ArmNetworkInterfaceName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedIPAddress, ok := stateBag.GetOk(constants.SSHHost)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.SSHHost)
|
|
||||||
}
|
|
||||||
|
|
||||||
if expectedIPAddress != "127.0.0.1" {
|
|
||||||
t.Fatalf("Expected the value of stateBag[%s] to be '127.0.0.1', but got '%s'.", constants.SSHHost, expectedIPAddress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepGetIPAddress() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmPublicIPAddressName, "Unit Test: PublicIPAddressName")
|
|
||||||
stateBag.Put(constants.ArmNicName, "Unit Test: NicName")
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepGetOSDisk struct {
|
|
||||||
client *AzureClient
|
|
||||||
query func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error)
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepGetOSDisk(client *AzureClient, ui packersdk.Ui) *StepGetOSDisk {
|
|
||||||
var step = &StepGetOSDisk{
|
|
||||||
client: client,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
}
|
|
||||||
|
|
||||||
step.query = step.queryCompute
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepGetOSDisk) queryCompute(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) {
|
|
||||||
vm, err := s.client.VirtualMachinesClient.Get(ctx, resourceGroupName, computeName, "")
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
return vm, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepGetOSDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
s.say("Querying the machine's properties ...")
|
|
||||||
|
|
||||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
var computeName = state.Get(constants.ArmComputeName).(string)
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
|
||||||
s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName))
|
|
||||||
|
|
||||||
vm, err := s.query(ctx, resourceGroupName, computeName)
|
|
||||||
if err != nil {
|
|
||||||
state.Put(constants.Error, err)
|
|
||||||
s.error(err)
|
|
||||||
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
var vhdUri string
|
|
||||||
if vm.StorageProfile.OsDisk.Vhd != nil {
|
|
||||||
vhdUri = *vm.StorageProfile.OsDisk.Vhd.URI
|
|
||||||
s.say(fmt.Sprintf(" -> OS Disk : '%s'", vhdUri))
|
|
||||||
} else {
|
|
||||||
vhdUri = *vm.StorageProfile.OsDisk.ManagedDisk.ID
|
|
||||||
s.say(fmt.Sprintf(" -> Managed OS Disk : '%s'", vhdUri))
|
|
||||||
}
|
|
||||||
|
|
||||||
state.Put(constants.ArmOSDiskVhd, vhdUri)
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepGetOSDisk) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,123 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepGetOSDiskShouldFailIfGetFails(t *testing.T) {
|
|
||||||
var testSubject = &StepGetOSDisk{
|
|
||||||
query: func(context.Context, string, string) (compute.VirtualMachine, error) {
|
|
||||||
return createVirtualMachineFromUri("test.vhd"), fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetOSDisk()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepGetOSDiskShouldPassIfGetPasses(t *testing.T) {
|
|
||||||
var testSubject = &StepGetOSDisk{
|
|
||||||
query: func(context.Context, string, string) (compute.VirtualMachine, error) {
|
|
||||||
return createVirtualMachineFromUri("test.vhd"), nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetOSDisk()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepGetOSDiskShouldTakeValidateArgumentsFromStateBag(t *testing.T) {
|
|
||||||
var actualResourceGroupName string
|
|
||||||
var actualComputeName string
|
|
||||||
|
|
||||||
var testSubject = &StepGetOSDisk{
|
|
||||||
query: func(ctx context.Context, resourceGroupName string, computeName string) (compute.VirtualMachine, error) {
|
|
||||||
actualResourceGroupName = resourceGroupName
|
|
||||||
actualComputeName = computeName
|
|
||||||
|
|
||||||
return createVirtualMachineFromUri("test.vhd"), nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepGetOSDisk()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string)
|
|
||||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
|
|
||||||
if actualComputeName != expectedComputeName {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualResourceGroupName != expectedResourceGroupName {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedOSDiskVhd, ok := stateBag.GetOk(constants.ArmOSDiskVhd)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.ArmOSDiskVhd)
|
|
||||||
}
|
|
||||||
|
|
||||||
if expectedOSDiskVhd != "test.vhd" {
|
|
||||||
t.Fatalf("Expected the value of stateBag[%s] to be 'test.vhd', but got '%s'.", constants.ArmOSDiskVhd, expectedOSDiskVhd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepGetOSDisk() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName")
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
||||||
|
|
||||||
func createVirtualMachineFromUri(vhdUri string) compute.VirtualMachine {
|
|
||||||
vm := compute.VirtualMachine{
|
|
||||||
VirtualMachineProperties: &compute.VirtualMachineProperties{
|
|
||||||
StorageProfile: &compute.StorageProfile{
|
|
||||||
OsDisk: &compute.OSDisk{
|
|
||||||
Vhd: &compute.VirtualHardDisk{
|
|
||||||
URI: &vhdUri,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return vm
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepPowerOffCompute struct {
|
|
||||||
client *AzureClient
|
|
||||||
powerOff func(ctx context.Context, resourceGroupName string, computeName string) error
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepPowerOffCompute(client *AzureClient, ui packersdk.Ui) *StepPowerOffCompute {
|
|
||||||
var step = &StepPowerOffCompute{
|
|
||||||
client: client,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
}
|
|
||||||
|
|
||||||
step.powerOff = step.powerOffCompute
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepPowerOffCompute) powerOffCompute(ctx context.Context, resourceGroupName string, computeName string) error {
|
|
||||||
f, err := s.client.VirtualMachinesClient.Deallocate(ctx, resourceGroupName, computeName)
|
|
||||||
if err == nil {
|
|
||||||
err = f.WaitForCompletionRef(ctx, s.client.VirtualMachinesClient.Client)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepPowerOffCompute) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
s.say("Powering off machine ...")
|
|
||||||
|
|
||||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
var computeName = state.Get(constants.ArmComputeName).(string)
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
|
||||||
s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName))
|
|
||||||
|
|
||||||
err := s.powerOff(ctx, resourceGroupName, computeName)
|
|
||||||
|
|
||||||
return processStepResult(err, s.error, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepPowerOffCompute) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepPowerOffComputeShouldFailIfPowerOffFails(t *testing.T) {
|
|
||||||
var testSubject = &StepPowerOffCompute{
|
|
||||||
powerOff: func(context.Context, string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepPowerOffCompute()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepPowerOffComputeShouldPassIfPowerOffPasses(t *testing.T) {
|
|
||||||
var testSubject = &StepPowerOffCompute{
|
|
||||||
powerOff: func(context.Context, string, string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepPowerOffCompute()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepPowerOffComputeShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|
||||||
var actualResourceGroupName string
|
|
||||||
var actualComputeName string
|
|
||||||
|
|
||||||
var testSubject = &StepPowerOffCompute{
|
|
||||||
powerOff: func(ctx context.Context, resourceGroupName string, computeName string) error {
|
|
||||||
actualResourceGroupName = resourceGroupName
|
|
||||||
actualComputeName = computeName
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepPowerOffCompute()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedComputeName = stateBag.Get(constants.ArmComputeName).(string)
|
|
||||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
|
|
||||||
if actualComputeName != expectedComputeName {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualResourceGroupName != expectedResourceGroupName {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepPowerOffCompute() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName")
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
|
@ -1,192 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest/date"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepPublishToSharedImageGallery struct {
|
|
||||||
client *AzureClient
|
|
||||||
publish func(ctx context.Context, mdiID string, sharedImageGallery SharedImageGalleryDestination, miSGImageVersionEndOfLifeDate string, miSGImageVersionExcludeFromLatest bool, miSigReplicaCount int32, location string, tags map[string]*string) (string, error)
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
toSIG func() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepPublishToSharedImageGallery(client *AzureClient, ui packersdk.Ui, config *Config) *StepPublishToSharedImageGallery {
|
|
||||||
var step = &StepPublishToSharedImageGallery{
|
|
||||||
client: client,
|
|
||||||
say: func(message string) {
|
|
||||||
ui.Say(message)
|
|
||||||
},
|
|
||||||
error: func(e error) {
|
|
||||||
ui.Error(e.Error())
|
|
||||||
},
|
|
||||||
toSIG: func() bool {
|
|
||||||
return config.isManagedImage() && config.SharedGalleryDestination.SigDestinationGalleryName != ""
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
step.publish = step.publishToSig
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSigDestinationStorageAccountType(s string) (compute.StorageAccountType, error) {
|
|
||||||
switch s {
|
|
||||||
case "", string(compute.StorageAccountTypeStandardLRS):
|
|
||||||
return compute.StorageAccountTypeStandardLRS, nil
|
|
||||||
case string(compute.StorageAccountTypeStandardZRS):
|
|
||||||
return compute.StorageAccountTypeStandardZRS, nil
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("not an accepted value for shared_image_gallery_destination.storage_account_type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSigDestination(state multistep.StateBag) SharedImageGalleryDestination {
|
|
||||||
subscription := state.Get(constants.ArmManagedImageSubscription).(string)
|
|
||||||
resourceGroup := state.Get(constants.ArmManagedImageSigPublishResourceGroup).(string)
|
|
||||||
galleryName := state.Get(constants.ArmManagedImageSharedGalleryName).(string)
|
|
||||||
imageName := state.Get(constants.ArmManagedImageSharedGalleryImageName).(string)
|
|
||||||
imageVersion := state.Get(constants.ArmManagedImageSharedGalleryImageVersion).(string)
|
|
||||||
replicationRegions := state.Get(constants.ArmManagedImageSharedGalleryReplicationRegions).([]string)
|
|
||||||
storageAccountType := state.Get(constants.ArmManagedImageSharedGalleryImageVersionStorageAccountType).(string)
|
|
||||||
|
|
||||||
return SharedImageGalleryDestination{
|
|
||||||
SigDestinationSubscription: subscription,
|
|
||||||
SigDestinationResourceGroup: resourceGroup,
|
|
||||||
SigDestinationGalleryName: galleryName,
|
|
||||||
SigDestinationImageName: imageName,
|
|
||||||
SigDestinationImageVersion: imageVersion,
|
|
||||||
SigDestinationReplicationRegions: replicationRegions,
|
|
||||||
SigDestinationStorageAccountType: storageAccountType,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, mdiID string, sharedImageGallery SharedImageGalleryDestination, miSGImageVersionEndOfLifeDate string, miSGImageVersionExcludeFromLatest bool, miSigReplicaCount int32, location string, tags map[string]*string) (string, error) {
|
|
||||||
replicationRegions := make([]compute.TargetRegion, len(sharedImageGallery.SigDestinationReplicationRegions))
|
|
||||||
for i, v := range sharedImageGallery.SigDestinationReplicationRegions {
|
|
||||||
regionName := v
|
|
||||||
replicationRegions[i] = compute.TargetRegion{Name: ®ionName}
|
|
||||||
}
|
|
||||||
|
|
||||||
var endOfLifeDate *date.Time
|
|
||||||
if miSGImageVersionEndOfLifeDate != "" {
|
|
||||||
parseDate, err := date.ParseTime("2006-01-02T15:04:05.99Z", miSGImageVersionEndOfLifeDate)
|
|
||||||
if err != nil {
|
|
||||||
s.say(fmt.Sprintf("Error parsing date from shared_gallery_image_version_end_of_life_date: %s", err))
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
endOfLifeDate = &date.Time{Time: parseDate}
|
|
||||||
} else {
|
|
||||||
endOfLifeDate = (*date.Time)(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
storageAccountType, err := getSigDestinationStorageAccountType(string(sharedImageGallery.SigDestinationStorageAccountType))
|
|
||||||
if err != nil {
|
|
||||||
s.error(err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
galleryImageVersion := compute.GalleryImageVersion{
|
|
||||||
Location: &location,
|
|
||||||
Tags: tags,
|
|
||||||
GalleryImageVersionProperties: &compute.GalleryImageVersionProperties{
|
|
||||||
PublishingProfile: &compute.GalleryImageVersionPublishingProfile{
|
|
||||||
Source: &compute.GalleryArtifactSource{
|
|
||||||
ManagedImage: &compute.ManagedArtifact{
|
|
||||||
ID: &mdiID,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TargetRegions: &replicationRegions,
|
|
||||||
EndOfLifeDate: endOfLifeDate,
|
|
||||||
ExcludeFromLatest: &miSGImageVersionExcludeFromLatest,
|
|
||||||
ReplicaCount: &miSigReplicaCount,
|
|
||||||
StorageAccountType: storageAccountType,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := s.client.GalleryImageVersionsClient.CreateOrUpdate(ctx, sharedImageGallery.SigDestinationResourceGroup, sharedImageGallery.SigDestinationGalleryName, sharedImageGallery.SigDestinationImageName, sharedImageGallery.SigDestinationImageVersion, galleryImageVersion)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = f.WaitForCompletionRef(ctx, s.client.GalleryImageVersionsClient.Client)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
createdSGImageVersion, err := f.Result(s.client.GalleryImageVersionsClient)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> Shared Gallery Image Version ID : '%s'", *(createdSGImageVersion.ID)))
|
|
||||||
return *(createdSGImageVersion.ID), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag multistep.StateBag) multistep.StepAction {
|
|
||||||
if !s.toSIG() {
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
s.say("Publishing to Shared Image Gallery ...")
|
|
||||||
|
|
||||||
location := stateBag.Get(constants.ArmLocation).(string)
|
|
||||||
tags := stateBag.Get(constants.ArmTags).(map[string]*string)
|
|
||||||
|
|
||||||
sharedImageGallery := getSigDestination(stateBag)
|
|
||||||
targetManagedImageResourceGroupName := stateBag.Get(constants.ArmManagedImageResourceGroupName).(string)
|
|
||||||
targetManagedImageName := stateBag.Get(constants.ArmManagedImageName).(string)
|
|
||||||
|
|
||||||
managedImageSubscription := stateBag.Get(constants.ArmManagedImageSubscription).(string)
|
|
||||||
mdiID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", managedImageSubscription, targetManagedImageResourceGroupName, targetManagedImageName)
|
|
||||||
|
|
||||||
miSGImageVersionEndOfLifeDate, _ := stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersionEndOfLifeDate).(string)
|
|
||||||
miSGImageVersionExcludeFromLatest, _ := stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersionExcludeFromLatest).(bool)
|
|
||||||
miSigReplicaCount, _ := stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersionReplicaCount).(int32)
|
|
||||||
// Replica count must be between 1 and 10 inclusive.
|
|
||||||
if miSigReplicaCount <= 0 {
|
|
||||||
miSigReplicaCount = constants.SharedImageGalleryImageVersionDefaultMinReplicaCount
|
|
||||||
} else if miSigReplicaCount > 10 {
|
|
||||||
miSigReplicaCount = constants.SharedImageGalleryImageVersionDefaultMaxReplicaCount
|
|
||||||
}
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> MDI ID used for SIG publish : '%s'", mdiID))
|
|
||||||
s.say(fmt.Sprintf(" -> SIG publish resource group : '%s'", sharedImageGallery.SigDestinationResourceGroup))
|
|
||||||
s.say(fmt.Sprintf(" -> SIG gallery name : '%s'", sharedImageGallery.SigDestinationGalleryName))
|
|
||||||
s.say(fmt.Sprintf(" -> SIG image name : '%s'", sharedImageGallery.SigDestinationImageName))
|
|
||||||
s.say(fmt.Sprintf(" -> SIG image version : '%s'", sharedImageGallery.SigDestinationImageVersion))
|
|
||||||
s.say(fmt.Sprintf(" -> SIG replication regions : '%v'", sharedImageGallery.SigDestinationReplicationRegions))
|
|
||||||
s.say(fmt.Sprintf(" -> SIG storage account type : '%s'", sharedImageGallery.SigDestinationStorageAccountType))
|
|
||||||
s.say(fmt.Sprintf(" -> SIG image version endoflife date : '%s'", miSGImageVersionEndOfLifeDate))
|
|
||||||
s.say(fmt.Sprintf(" -> SIG image version exclude from latest : '%t'", miSGImageVersionExcludeFromLatest))
|
|
||||||
s.say(fmt.Sprintf(" -> SIG replica count [1, 10] : '%d'", miSigReplicaCount))
|
|
||||||
|
|
||||||
createdGalleryImageVersionID, err := s.publish(ctx, mdiID, sharedImageGallery, miSGImageVersionEndOfLifeDate, miSGImageVersionExcludeFromLatest, miSigReplicaCount, location, tags)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
stateBag.Put(constants.Error, err)
|
|
||||||
s.error(err)
|
|
||||||
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryId, createdGalleryImageVersionID)
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepPublishToSharedImageGallery) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepPublishToSharedImageGalleryShouldNotPublishForVhd(t *testing.T) {
|
|
||||||
var testSubject = &StepPublishToSharedImageGallery{
|
|
||||||
publish: func(context.Context, string, SharedImageGalleryDestination, string, bool, int32, string, map[string]*string) (string, error) {
|
|
||||||
return "test", nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
toSIG: func() bool { return false },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepPublishToSharedImageGalleryForVhd()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepPublishToSharedImageGalleryShouldPublishForManagedImageWithSig(t *testing.T) {
|
|
||||||
var testSubject = &StepPublishToSharedImageGallery{
|
|
||||||
publish: func(context.Context, string, SharedImageGalleryDestination, string, bool, int32, string, map[string]*string) (string, error) {
|
|
||||||
return "", nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
toSIG: func() bool { return true },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepPublishToSharedImageGallery()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepPublishToSharedImageGallery() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmManagedImageSigPublishResourceGroup, "Unit Test: ManagedImageSigPublishResourceGroup")
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryName, "Unit Test: ManagedImageSharedGalleryName")
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryImageName, "Unit Test: ManagedImageSharedGalleryImageName")
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersion, "Unit Test: ManagedImageSharedGalleryImageVersion")
|
|
||||||
stateBag.Put(constants.ArmLocation, "Unit Test: Location")
|
|
||||||
value := "Unit Test: Tags"
|
|
||||||
tags := map[string]*string{
|
|
||||||
"tag01": &value,
|
|
||||||
}
|
|
||||||
stateBag.Put(constants.ArmTags, tags)
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryReplicationRegions, []string{"ManagedImageSharedGalleryReplicationRegionA", "ManagedImageSharedGalleryReplicationRegionB"})
|
|
||||||
stateBag.Put(constants.ArmManagedImageSharedGalleryImageVersionStorageAccountType, "Standard_LRS")
|
|
||||||
stateBag.Put(constants.ArmManagedImageResourceGroupName, "Unit Test: ManagedImageResourceGroupName")
|
|
||||||
stateBag.Put(constants.ArmManagedImageName, "Unit Test: ManagedImageName")
|
|
||||||
stateBag.Put(constants.ArmManagedImageSubscription, "Unit Test: ManagedImageSubscription")
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepPublishToSharedImageGalleryForVhd() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmLocation, "Unit Test: Location")
|
|
||||||
value := "Unit Test: Tags"
|
|
||||||
tags := map[string]*string{
|
|
||||||
"tag01": &value,
|
|
||||||
}
|
|
||||||
stateBag.Put(constants.ArmTags, tags)
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepSetCertificate struct {
|
|
||||||
config *Config
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepSetCertificate(config *Config, ui packersdk.Ui) *StepSetCertificate {
|
|
||||||
var step = &StepSetCertificate{
|
|
||||||
config: config,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
}
|
|
||||||
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepSetCertificate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
s.say("Setting the certificate's URL ...")
|
|
||||||
|
|
||||||
var winRMCertificateUrl = state.Get(constants.ArmCertificateUrl).(string)
|
|
||||||
s.config.tmpWinRMCertificateUrl = winRMCertificateUrl
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepSetCertificate) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepSetCertificateShouldPassIfGetPasses(t *testing.T) {
|
|
||||||
var testSubject = &StepSetCertificate{
|
|
||||||
config: new(Config),
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepSetCertificate()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSetCertificateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|
||||||
config := new(Config)
|
|
||||||
var testSubject = &StepSetCertificate{
|
|
||||||
config: config,
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepSetCertificate()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.tmpWinRMCertificateUrl != stateBag.Get(constants.ArmCertificateUrl) {
|
|
||||||
t.Fatalf("Expected config.tmpWinRMCertificateUrl to be %s, but got %s'", stateBag.Get(constants.ArmCertificateUrl), config.tmpWinRMCertificateUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepSetCertificate() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
stateBag.Put(constants.ArmCertificateUrl, "Unit Test: Certificate URL")
|
|
||||||
return stateBag
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest/to"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepSnapshotDataDisks struct {
|
|
||||||
client *AzureClient
|
|
||||||
create func(ctx context.Context, resourceGroupName string, srcUriVhd string, location string, tags map[string]*string, dstSnapshotName string) error
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
enable func() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepSnapshotDataDisks(client *AzureClient, ui packersdk.Ui, config *Config) *StepSnapshotDataDisks {
|
|
||||||
var step = &StepSnapshotDataDisks{
|
|
||||||
client: client,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
enable: func() bool { return config.isManagedImage() && config.ManagedImageDataDiskSnapshotPrefix != "" },
|
|
||||||
}
|
|
||||||
|
|
||||||
step.create = step.createDataDiskSnapshot
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepSnapshotDataDisks) createDataDiskSnapshot(ctx context.Context, resourceGroupName string, srcUriVhd string, location string, tags map[string]*string, dstSnapshotName string) error {
|
|
||||||
|
|
||||||
srcVhdToSnapshot := compute.Snapshot{
|
|
||||||
DiskProperties: &compute.DiskProperties{
|
|
||||||
CreationData: &compute.CreationData{
|
|
||||||
CreateOption: compute.Copy,
|
|
||||||
SourceResourceID: to.StringPtr(srcUriVhd),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: to.StringPtr(location),
|
|
||||||
Tags: tags,
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := s.client.SnapshotsClient.CreateOrUpdate(ctx, resourceGroupName, dstSnapshotName, srcVhdToSnapshot)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = f.WaitForCompletionRef(ctx, s.client.SnapshotsClient.Client)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
createdSnapshot, err := f.Result(s.client.SnapshotsClient)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> Snapshot ID : '%s'", *(createdSnapshot.ID)))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepSnapshotDataDisks) Run(ctx context.Context, stateBag multistep.StateBag) multistep.StepAction {
|
|
||||||
if !s.enable() {
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
var resourceGroupName = stateBag.Get(constants.ArmManagedImageResourceGroupName).(string)
|
|
||||||
var location = stateBag.Get(constants.ArmLocation).(string)
|
|
||||||
var tags = stateBag.Get(constants.ArmTags).(map[string]*string)
|
|
||||||
var additionalDisks = stateBag.Get(constants.ArmAdditionalDiskVhds).([]string)
|
|
||||||
var dstSnapshotPrefix = stateBag.Get(constants.ArmManagedImageDataDiskSnapshotPrefix).(string)
|
|
||||||
|
|
||||||
if len(additionalDisks) == 1 {
|
|
||||||
s.say(fmt.Sprintf("Snapshotting data disk ..."))
|
|
||||||
} else {
|
|
||||||
s.say(fmt.Sprintf("Snapshotting data disks ..."))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, disk := range additionalDisks {
|
|
||||||
s.say(fmt.Sprintf(" -> Data Disk : '%s'", disk))
|
|
||||||
|
|
||||||
dstSnapshotName := dstSnapshotPrefix + strconv.Itoa(i)
|
|
||||||
err := s.create(ctx, resourceGroupName, disk, location, tags, dstSnapshotName)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
stateBag.Put(constants.Error, err)
|
|
||||||
s.error(err)
|
|
||||||
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepSnapshotDataDisks) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepSnapshotDataDisksShouldFailIfSnapshotFails(t *testing.T) {
|
|
||||||
var testSubject = &StepSnapshotDataDisks{
|
|
||||||
create: func(context.Context, string, string, string, map[string]*string, string) error {
|
|
||||||
return fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
enable: func() bool { return true },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepSnapshotDataDisks()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSnapshotDataDisksShouldNotExecute(t *testing.T) {
|
|
||||||
var testSubject = &StepSnapshotDataDisks{
|
|
||||||
create: func(context.Context, string, string, string, map[string]*string, string) error {
|
|
||||||
return fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
enable: func() bool { return false },
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), nil)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSnapshotDataDisksShouldPassIfSnapshotPasses(t *testing.T) {
|
|
||||||
var testSubject = &StepSnapshotDataDisks{
|
|
||||||
create: func(context.Context, string, string, string, map[string]*string, string) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
enable: func() bool { return true },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepSnapshotDataDisks()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepSnapshotDataDisks() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmManagedImageResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
stateBag.Put(constants.ArmLocation, "Unit Test: Location")
|
|
||||||
|
|
||||||
value := "Unit Test: Tags"
|
|
||||||
tags := map[string]*string{
|
|
||||||
"tag01": &value,
|
|
||||||
}
|
|
||||||
stateBag.Put(constants.ArmTags, tags)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmAdditionalDiskVhds, []string{"subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk"})
|
|
||||||
stateBag.Put(constants.ArmManagedImageDataDiskSnapshotPrefix, "Unit Test: ManagedImageDataDiskSnapshotPrefix")
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest/to"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepSnapshotOSDisk struct {
|
|
||||||
client *AzureClient
|
|
||||||
create func(ctx context.Context, resourceGroupName string, srcUriVhd string, location string, tags map[string]*string, dstSnapshotName string) error
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
enable func() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepSnapshotOSDisk(client *AzureClient, ui packersdk.Ui, config *Config) *StepSnapshotOSDisk {
|
|
||||||
var step = &StepSnapshotOSDisk{
|
|
||||||
client: client,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
enable: func() bool { return config.isManagedImage() && config.ManagedImageOSDiskSnapshotName != "" },
|
|
||||||
}
|
|
||||||
|
|
||||||
step.create = step.createSnapshot
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepSnapshotOSDisk) createSnapshot(ctx context.Context, resourceGroupName string, srcUriVhd string, location string, tags map[string]*string, dstSnapshotName string) error {
|
|
||||||
|
|
||||||
srcVhdToSnapshot := compute.Snapshot{
|
|
||||||
DiskProperties: &compute.DiskProperties{
|
|
||||||
CreationData: &compute.CreationData{
|
|
||||||
CreateOption: compute.Copy,
|
|
||||||
SourceResourceID: to.StringPtr(srcUriVhd),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Location: to.StringPtr(location),
|
|
||||||
Tags: tags,
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := s.client.SnapshotsClient.CreateOrUpdate(ctx, resourceGroupName, dstSnapshotName, srcVhdToSnapshot)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = f.WaitForCompletionRef(ctx, s.client.SnapshotsClient.Client)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
createdSnapshot, err := f.Result(s.client.SnapshotsClient)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> Snapshot ID : '%s'", *(createdSnapshot.ID)))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepSnapshotOSDisk) Run(ctx context.Context, stateBag multistep.StateBag) multistep.StepAction {
|
|
||||||
if !s.enable() {
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
s.say("Snapshotting OS disk ...")
|
|
||||||
|
|
||||||
var resourceGroupName = stateBag.Get(constants.ArmManagedImageResourceGroupName).(string)
|
|
||||||
var location = stateBag.Get(constants.ArmLocation).(string)
|
|
||||||
var tags = stateBag.Get(constants.ArmTags).(map[string]*string)
|
|
||||||
var srcUriVhd = stateBag.Get(constants.ArmOSDiskVhd).(string)
|
|
||||||
var dstSnapshotName = stateBag.Get(constants.ArmManagedImageOSDiskSnapshotName).(string)
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> OS Disk : '%s'", srcUriVhd))
|
|
||||||
err := s.create(ctx, resourceGroupName, srcUriVhd, location, tags, dstSnapshotName)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
stateBag.Put(constants.Error, err)
|
|
||||||
s.error(err)
|
|
||||||
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepSnapshotOSDisk) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepSnapshotOSDiskShouldFailIfSnapshotFails(t *testing.T) {
|
|
||||||
var testSubject = &StepSnapshotOSDisk{
|
|
||||||
create: func(context.Context, string, string, string, map[string]*string, string) error {
|
|
||||||
return fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
enable: func() bool { return true },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepSnapshotOSDisk()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSnapshotOSDiskShouldNotExecute(t *testing.T) {
|
|
||||||
var testSubject = &StepSnapshotOSDisk{
|
|
||||||
create: func(context.Context, string, string, string, map[string]*string, string) error {
|
|
||||||
return fmt.Errorf("!! Unit Test FAIL !!")
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
enable: func() bool { return false },
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), nil)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSnapshotOSDiskShouldPassIfSnapshotPasses(t *testing.T) {
|
|
||||||
var testSubject = &StepSnapshotOSDisk{
|
|
||||||
create: func(context.Context, string, string, string, map[string]*string, string) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
enable: func() bool { return true },
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepSnapshotOSDisk()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepSnapshotOSDisk() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmManagedImageResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
stateBag.Put(constants.ArmLocation, "Unit Test: Location")
|
|
||||||
|
|
||||||
value := "Unit Test: Tags"
|
|
||||||
tags := map[string]*string{
|
|
||||||
"tag01": &value,
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmTags, tags)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmOSDiskVhd, "subscriptions/123-456-789/resourceGroups/existingresourcegroup/providers/Microsoft.Compute/disks/osdisk")
|
|
||||||
stateBag.Put(constants.ArmManagedImageOSDiskSnapshotName, "Unit Test: ManagedImageOSDiskSnapshotName")
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestProcessStepResultShouldContinueForNonErrors(t *testing.T) {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
code := processStepResult(nil, func(error) { t.Fatal("Should not be called!") }, stateBag)
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok {
|
|
||||||
t.Errorf("Error was nil, but was still in the state bag.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if code != multistep.ActionContinue {
|
|
||||||
t.Errorf("Expected ActionContinue(%d), but got=%d", multistep.ActionContinue, code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProcessStepResultShouldHaltOnError(t *testing.T) {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
isSaidError := false
|
|
||||||
|
|
||||||
code := processStepResult(fmt.Errorf("boom"), func(error) { isSaidError = true }, stateBag)
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); !ok {
|
|
||||||
t.Errorf("Error was non nil, but was not in the state bag.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isSaidError {
|
|
||||||
t.Errorf("Expected error to be said, but it was not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if code != multistep.ActionHalt {
|
|
||||||
t.Errorf("Expected ActionHalt(%d), but got=%d", multistep.ActionHalt, code)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepValidateTemplate struct {
|
|
||||||
client *AzureClient
|
|
||||||
validate func(ctx context.Context, resourceGroupName string, deploymentName string) error
|
|
||||||
say func(message string)
|
|
||||||
error func(e error)
|
|
||||||
config *Config
|
|
||||||
factory templateFactoryFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStepValidateTemplate(client *AzureClient, ui packersdk.Ui, config *Config, factory templateFactoryFunc) *StepValidateTemplate {
|
|
||||||
var step = &StepValidateTemplate{
|
|
||||||
client: client,
|
|
||||||
say: func(message string) { ui.Say(message) },
|
|
||||||
error: func(e error) { ui.Error(e.Error()) },
|
|
||||||
config: config,
|
|
||||||
factory: factory,
|
|
||||||
}
|
|
||||||
|
|
||||||
step.validate = step.validateTemplate
|
|
||||||
return step
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepValidateTemplate) validateTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error {
|
|
||||||
deployment, err := s.factory(s.config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = s.client.DeploymentsClient.Validate(ctx, resourceGroupName, deploymentName, *deployment)
|
|
||||||
if err != nil {
|
|
||||||
s.say(s.client.LastError.Error())
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepValidateTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
s.say("Validating deployment template ...")
|
|
||||||
|
|
||||||
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
var deploymentName = state.Get(constants.ArmDeploymentName).(string)
|
|
||||||
|
|
||||||
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
|
|
||||||
s.say(fmt.Sprintf(" -> DeploymentName : '%s'", deploymentName))
|
|
||||||
|
|
||||||
err := s.validate(ctx, resourceGroupName, deploymentName)
|
|
||||||
return processStepResult(err, s.error, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepValidateTemplate) Cleanup(multistep.StateBag) {
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepValidateTemplateShouldFailIfValidateFails(t *testing.T) {
|
|
||||||
var testSubject = &StepValidateTemplate{
|
|
||||||
validate: func(context.Context, string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepValidateTemplate()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionHalt {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionHalt', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == false {
|
|
||||||
t.Fatalf("Expected the step to set stateBag['%s'], but it was not.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepValidateTemplateShouldPassIfValidatePasses(t *testing.T) {
|
|
||||||
var testSubject = &StepValidateTemplate{
|
|
||||||
validate: func(context.Context, string, string) error { return nil },
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepValidateTemplate()
|
|
||||||
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := stateBag.GetOk(constants.Error); ok == true {
|
|
||||||
t.Fatalf("Expected the step to not set stateBag['%s'], but it was.", constants.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepValidateTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
|
|
||||||
var actualResourceGroupName string
|
|
||||||
var actualDeploymentName string
|
|
||||||
|
|
||||||
var testSubject = &StepValidateTemplate{
|
|
||||||
validate: func(ctx context.Context, resourceGroupName string, deploymentName string) error {
|
|
||||||
actualResourceGroupName = resourceGroupName
|
|
||||||
actualDeploymentName = deploymentName
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
say: func(message string) {},
|
|
||||||
error: func(e error) {},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBag := createTestStateBagStepValidateTemplate()
|
|
||||||
var result = testSubject.Run(context.Background(), stateBag)
|
|
||||||
|
|
||||||
if result != multistep.ActionContinue {
|
|
||||||
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedDeploymentName = stateBag.Get(constants.ArmDeploymentName).(string)
|
|
||||||
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
|
|
||||||
|
|
||||||
if actualDeploymentName != expectedDeploymentName {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmDeploymentName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualResourceGroupName != expectedResourceGroupName {
|
|
||||||
t.Fatal("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestStateBagStepValidateTemplate() multistep.StateBag {
|
|
||||||
stateBag := new(multistep.BasicStateBag)
|
|
||||||
|
|
||||||
stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName")
|
|
||||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
|
||||||
|
|
||||||
return stateBag
|
|
||||||
}
|
|
|
@ -1,213 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
type templateFactoryFunc func(*Config) (*resources.Deployment, error)
|
|
||||||
|
|
||||||
func GetKeyVaultDeployment(config *Config) (*resources.Deployment, error) {
|
|
||||||
params := &template.TemplateParameters{
|
|
||||||
KeyVaultName: &template.TemplateParameter{Value: config.tmpKeyVaultName},
|
|
||||||
KeyVaultSKU: &template.TemplateParameter{Value: config.BuildKeyVaultSKU},
|
|
||||||
KeyVaultSecretValue: &template.TemplateParameter{Value: config.winrmCertificate},
|
|
||||||
ObjectId: &template.TemplateParameter{Value: config.ClientConfig.ObjectID},
|
|
||||||
TenantId: &template.TemplateParameter{Value: config.ClientConfig.TenantID},
|
|
||||||
}
|
|
||||||
|
|
||||||
builder, _ := template.NewTemplateBuilder(template.KeyVault)
|
|
||||||
builder.SetTags(&config.AzureTags)
|
|
||||||
|
|
||||||
doc, _ := builder.ToJSON()
|
|
||||||
return createDeploymentParameters(*doc, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) {
|
|
||||||
params := &template.TemplateParameters{
|
|
||||||
AdminUsername: &template.TemplateParameter{Value: config.UserName},
|
|
||||||
AdminPassword: &template.TemplateParameter{Value: config.Password},
|
|
||||||
DnsNameForPublicIP: &template.TemplateParameter{Value: config.tmpComputeName},
|
|
||||||
NicName: &template.TemplateParameter{Value: config.tmpNicName},
|
|
||||||
OSDiskName: &template.TemplateParameter{Value: config.tmpOSDiskName},
|
|
||||||
DataDiskName: &template.TemplateParameter{Value: config.tmpDataDiskName},
|
|
||||||
PublicIPAddressName: &template.TemplateParameter{Value: config.tmpPublicIPAddressName},
|
|
||||||
SubnetName: &template.TemplateParameter{Value: config.tmpSubnetName},
|
|
||||||
StorageAccountBlobEndpoint: &template.TemplateParameter{Value: config.storageAccountBlobEndpoint},
|
|
||||||
VirtualNetworkName: &template.TemplateParameter{Value: config.tmpVirtualNetworkName},
|
|
||||||
NsgName: &template.TemplateParameter{Value: config.tmpNsgName},
|
|
||||||
VMSize: &template.TemplateParameter{Value: config.VMSize},
|
|
||||||
VMName: &template.TemplateParameter{Value: config.tmpComputeName},
|
|
||||||
}
|
|
||||||
|
|
||||||
builder, err := template.NewTemplateBuilder(template.BasicTemplate)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
osType := compute.Linux
|
|
||||||
|
|
||||||
switch config.OSType {
|
|
||||||
case constants.Target_Linux:
|
|
||||||
err = builder.BuildLinux(config.sshAuthorizedKey, config.Comm.SSHPassword == "") // if ssh password is not explicitly specified, disable password auth
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case constants.Target_Windows:
|
|
||||||
osType = compute.Windows
|
|
||||||
err = builder.BuildWindows(config.tmpKeyVaultName, config.tmpWinRMCertificateUrl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(config.UserAssignedManagedIdentities) != 0 {
|
|
||||||
if err := builder.SetIdentity(config.UserAssignedManagedIdentities); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.ImageUrl != "" {
|
|
||||||
err = builder.SetImageUrl(config.ImageUrl, osType, config.diskCachingType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else if config.CustomManagedImageName != "" {
|
|
||||||
err = builder.SetManagedDiskUrl(config.customManagedImageID, config.managedImageStorageAccountType, config.diskCachingType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else if config.ManagedImageName != "" && config.ImagePublisher != "" {
|
|
||||||
imageID := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/ArtifactTypes/vmimage/offers/%s/skus/%s/versions/%s",
|
|
||||||
config.ClientConfig.SubscriptionID,
|
|
||||||
config.Location,
|
|
||||||
config.ImagePublisher,
|
|
||||||
config.ImageOffer,
|
|
||||||
config.ImageSku,
|
|
||||||
config.ImageVersion)
|
|
||||||
|
|
||||||
builder.SetManagedMarketplaceImage(config.Location, config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, imageID, config.managedImageStorageAccountType, config.diskCachingType)
|
|
||||||
} else if config.SharedGallery.Subscription != "" {
|
|
||||||
imageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/galleries/%s/images/%s",
|
|
||||||
config.SharedGallery.Subscription,
|
|
||||||
config.SharedGallery.ResourceGroup,
|
|
||||||
config.SharedGallery.GalleryName,
|
|
||||||
config.SharedGallery.ImageName)
|
|
||||||
if config.SharedGallery.ImageVersion != "" {
|
|
||||||
imageID += fmt.Sprintf("/versions/%s",
|
|
||||||
config.SharedGallery.ImageVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = builder.SetSharedGalleryImage(config.Location, imageID, config.diskCachingType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = builder.SetMarketPlaceImage(config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, config.diskCachingType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.OSDiskSizeGB > 0 {
|
|
||||||
err = builder.SetOSDiskSizeGB(config.OSDiskSizeGB)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(config.AdditionalDiskSize) > 0 {
|
|
||||||
isManaged := config.CustomManagedImageName != "" || (config.ManagedImageName != "" && config.ImagePublisher != "") || config.SharedGallery.Subscription != ""
|
|
||||||
err = builder.SetAdditionalDisks(config.AdditionalDiskSize, config.tmpDataDiskName, isManaged, config.diskCachingType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.customData != "" {
|
|
||||||
err = builder.SetCustomData(config.customData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.PlanInfo.PlanName != "" {
|
|
||||||
err = builder.SetPlanInfo(config.PlanInfo.PlanName, config.PlanInfo.PlanProduct, config.PlanInfo.PlanPublisher, config.PlanInfo.PlanPromotionCode)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.VirtualNetworkName != "" && DefaultPrivateVirtualNetworkWithPublicIp != config.PrivateVirtualNetworkWithPublicIp {
|
|
||||||
err = builder.SetPrivateVirtualNetworkWithPublicIp(
|
|
||||||
config.VirtualNetworkResourceGroupName,
|
|
||||||
config.VirtualNetworkName,
|
|
||||||
config.VirtualNetworkSubnetName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else if config.VirtualNetworkName != "" {
|
|
||||||
err = builder.SetVirtualNetwork(
|
|
||||||
config.VirtualNetworkResourceGroupName,
|
|
||||||
config.VirtualNetworkName,
|
|
||||||
config.VirtualNetworkSubnetName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.AllowedInboundIpAddresses != nil && len(config.AllowedInboundIpAddresses) >= 1 && config.Comm.Port() != 0 {
|
|
||||||
err = builder.SetNetworkSecurityGroup(config.AllowedInboundIpAddresses, config.Comm.Port())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.BootDiagSTGAccount != "" {
|
|
||||||
err = builder.SetBootDiagnostics(config.BootDiagSTGAccount)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = builder.SetTags(&config.AzureTags)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
doc, _ := builder.ToJSON()
|
|
||||||
return createDeploymentParameters(*doc, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createDeploymentParameters(doc string, parameters *template.TemplateParameters) (*resources.Deployment, error) {
|
|
||||||
var template map[string]interface{}
|
|
||||||
err := json.Unmarshal(([]byte)(doc), &template)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
bs, err := json.Marshal(*parameters)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var templateParameters map[string]interface{}
|
|
||||||
err = json.Unmarshal(bs, &templateParameters)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &resources.Deployment{
|
|
||||||
Properties: &resources.DeploymentProperties{
|
|
||||||
Mode: resources.Incremental,
|
|
||||||
Template: &template,
|
|
||||||
Parameters: &templateParameters,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
|
|
||||||
"contentVersion": "1.0.0.0",
|
|
||||||
"parameters": {
|
|
||||||
"keyVaultName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"keyVaultSKU": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"keyVaultSecretValue": {
|
|
||||||
"type": "securestring"
|
|
||||||
},
|
|
||||||
"objectId": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"tenantId": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('keyVaultName')]",
|
|
||||||
"properties": {
|
|
||||||
"accessPolicies": [
|
|
||||||
{
|
|
||||||
"objectId": "[parameters('objectId')]",
|
|
||||||
"permissions": {
|
|
||||||
"keys": [
|
|
||||||
"all"
|
|
||||||
],
|
|
||||||
"secrets": [
|
|
||||||
"all"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"tenantId": "[parameters('tenantId')]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"enableSoftDelete": "true",
|
|
||||||
"enabledForDeployment": "true",
|
|
||||||
"enabledForTemplateDeployment": "true",
|
|
||||||
"sku": {
|
|
||||||
"family": "A",
|
|
||||||
"name": "[parameters('keyVaultSKU')]"
|
|
||||||
},
|
|
||||||
"tenantId": "[parameters('tenantId')]"
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]"
|
|
||||||
],
|
|
||||||
"name": "[variables('keyVaultSecretName')]",
|
|
||||||
"properties": {
|
|
||||||
"value": "[parameters('keyVaultSecretValue')]"
|
|
||||||
},
|
|
||||||
"type": "secrets"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": {
|
|
||||||
"tag01": "value01",
|
|
||||||
"tag02": "value02",
|
|
||||||
"tag03": "value03"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.KeyVault/vaults"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"apiVersion": "2015-06-01",
|
|
||||||
"keyVaultSecretName": "packerKeyVaultSecret",
|
|
||||||
"location": "[resourceGroup().location]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,211 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"PlanInfo": "planName00",
|
|
||||||
"PlanProduct": "planProduct00",
|
|
||||||
"PlanPromotionCode": "",
|
|
||||||
"PlanPublisher": "planPublisher00"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[variables('virtualNetworkName')]",
|
|
||||||
"properties": {
|
|
||||||
"addressSpace": {
|
|
||||||
"addressPrefixes": [
|
|
||||||
"[variables('addressPrefix')]"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"subnets": [
|
|
||||||
{
|
|
||||||
"name": "[variables('subnetName')]",
|
|
||||||
"properties": {
|
|
||||||
"addressPrefix": "[variables('subnetAddressPrefix')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"PlanInfo": "planName00",
|
|
||||||
"PlanProduct": "planProduct00",
|
|
||||||
"PlanPromotionCode": "",
|
|
||||||
"PlanPublisher": "planPublisher00"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/virtualNetworks"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"PlanInfo": "planName00",
|
|
||||||
"PlanProduct": "planProduct00",
|
|
||||||
"PlanPromotionCode": "",
|
|
||||||
"PlanPublisher": "planPublisher00"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"plan": {
|
|
||||||
"name": "planName00",
|
|
||||||
"product": "planProduct00",
|
|
||||||
"publisher": "planPublisher00"
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"disablePasswordAuthentication": true,
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"imageReference": {
|
|
||||||
"offer": "ignored00",
|
|
||||||
"publisher": "ignored00",
|
|
||||||
"sku": "ignored00",
|
|
||||||
"version": "latest"
|
|
||||||
},
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"vhd": {
|
|
||||||
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"PlanInfo": "planName00",
|
|
||||||
"PlanProduct": "planProduct00",
|
|
||||||
"PlanPromotionCode": "",
|
|
||||||
"PlanPublisher": "planPublisher00"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,216 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"PlanInfo": "planName00",
|
|
||||||
"PlanProduct": "planProduct00",
|
|
||||||
"PlanPromotionCode": "planPromotionCode00",
|
|
||||||
"PlanPublisher": "planPublisher00",
|
|
||||||
"dept": "engineering"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[variables('virtualNetworkName')]",
|
|
||||||
"properties": {
|
|
||||||
"addressSpace": {
|
|
||||||
"addressPrefixes": [
|
|
||||||
"[variables('addressPrefix')]"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"subnets": [
|
|
||||||
{
|
|
||||||
"name": "[variables('subnetName')]",
|
|
||||||
"properties": {
|
|
||||||
"addressPrefix": "[variables('subnetAddressPrefix')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"PlanInfo": "planName00",
|
|
||||||
"PlanProduct": "planProduct00",
|
|
||||||
"PlanPromotionCode": "planPromotionCode00",
|
|
||||||
"PlanPublisher": "planPublisher00",
|
|
||||||
"dept": "engineering"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/virtualNetworks"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"PlanInfo": "planName00",
|
|
||||||
"PlanProduct": "planProduct00",
|
|
||||||
"PlanPromotionCode": "planPromotionCode00",
|
|
||||||
"PlanPublisher": "planPublisher00",
|
|
||||||
"dept": "engineering"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"plan": {
|
|
||||||
"name": "planName00",
|
|
||||||
"product": "planProduct00",
|
|
||||||
"promotionCode": "planPromotionCode00",
|
|
||||||
"publisher": "planPublisher00"
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"disablePasswordAuthentication": true,
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"imageReference": {
|
|
||||||
"offer": "ignored00",
|
|
||||||
"publisher": "ignored00",
|
|
||||||
"sku": "ignored00",
|
|
||||||
"version": "latest"
|
|
||||||
},
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"vhd": {
|
|
||||||
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"PlanInfo": "planName00",
|
|
||||||
"PlanProduct": "planProduct00",
|
|
||||||
"PlanPromotionCode": "planPromotionCode00",
|
|
||||||
"PlanPublisher": "planPublisher00",
|
|
||||||
"dept": "engineering"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,182 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"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('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminPassword": "[parameters('adminPassword')]",
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"imageReference": {
|
|
||||||
"offer": "ImageOffer",
|
|
||||||
"publisher": "ImagePublisher",
|
|
||||||
"sku": "ImageSku",
|
|
||||||
"version": "ImageVersion"
|
|
||||||
},
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"vhd": {
|
|
||||||
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,180 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"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('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"disablePasswordAuthentication": true,
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"image": {
|
|
||||||
"uri": "https://localhost/custom.vhd"
|
|
||||||
},
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"osType": "Linux",
|
|
||||||
"vhd": {
|
|
||||||
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,141 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminPassword": "[parameters('adminPassword')]",
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"image": {
|
|
||||||
"uri": "https://localhost/custom.vhd"
|
|
||||||
},
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"osType": "Linux",
|
|
||||||
"vhd": {
|
|
||||||
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "virtualNetworkSubnetName",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "virtualNetworkName",
|
|
||||||
"virtualNetworkResourceGroup": "virtualNetworkResourceGroupName",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,200 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"tag01": "value01",
|
|
||||||
"tag02": "value02",
|
|
||||||
"tag03": "value03"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[variables('virtualNetworkName')]",
|
|
||||||
"properties": {
|
|
||||||
"addressSpace": {
|
|
||||||
"addressPrefixes": [
|
|
||||||
"[variables('addressPrefix')]"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"subnets": [
|
|
||||||
{
|
|
||||||
"name": "[variables('subnetName')]",
|
|
||||||
"properties": {
|
|
||||||
"addressPrefix": "[variables('subnetAddressPrefix')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"tag01": "value01",
|
|
||||||
"tag02": "value02",
|
|
||||||
"tag03": "value03"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/virtualNetworks"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"tag01": "value01",
|
|
||||||
"tag02": "value02",
|
|
||||||
"tag03": "value03"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"disablePasswordAuthentication": true,
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"image": {
|
|
||||||
"uri": "https://localhost/custom.vhd"
|
|
||||||
},
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"osType": "Linux",
|
|
||||||
"vhd": {
|
|
||||||
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"tag01": "value01",
|
|
||||||
"tag02": "value02",
|
|
||||||
"tag03": "value03"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,181 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"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('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('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": "[parameters('osDiskName')]",
|
|
||||||
"osType": "Linux",
|
|
||||||
"vhd": {
|
|
||||||
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,180 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"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('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"disablePasswordAuthentication": true,
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"imageReference": {
|
|
||||||
"id": ""
|
|
||||||
},
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"managedDisk": {
|
|
||||||
"storageAccountType": "Standard_LRS"
|
|
||||||
},
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"osType": "Linux"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,183 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"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('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminPassword": "[parameters('adminPassword')]",
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"imageReference": {
|
|
||||||
"offer": "--image-offer--",
|
|
||||||
"publisher": "--image-publisher--",
|
|
||||||
"sku": "--image-sku--",
|
|
||||||
"version": "--version--"
|
|
||||||
},
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"managedDisk": {
|
|
||||||
"storageAccountType": "Standard_LRS"
|
|
||||||
},
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"osType": "Linux"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,161 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"disablePasswordAuthentication": true,
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"imageReference": {
|
|
||||||
"offer": "--image-offer--",
|
|
||||||
"publisher": "--image-publisher--",
|
|
||||||
"sku": "--image-sku--",
|
|
||||||
"version": "--version--"
|
|
||||||
},
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"managedDisk": {
|
|
||||||
"storageAccountType": "Standard_LRS"
|
|
||||||
},
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"osType": "Linux"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "--virtual_network_subnet_name--",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "--virtual_network_name--",
|
|
||||||
"virtualNetworkResourceGroup": "--virtual_network_resource_group_name--",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,194 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"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('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminPassword": "[parameters('adminPassword')]",
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"dataDisks": [
|
|
||||||
{
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "Empty",
|
|
||||||
"diskSizeGB": 32,
|
|
||||||
"lun": 0,
|
|
||||||
"name": "[concat(parameters('dataDiskName'),'-1')]",
|
|
||||||
"vhd": {
|
|
||||||
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/',parameters('dataDiskName'),'-1','.vhd')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"imageReference": {
|
|
||||||
"offer": "--image-offer--",
|
|
||||||
"publisher": "--image-publisher--",
|
|
||||||
"sku": "--image-sku--",
|
|
||||||
"version": "--version--"
|
|
||||||
},
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"vhd": {
|
|
||||||
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,195 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"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('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"disablePasswordAuthentication": true,
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"dataDisks": [
|
|
||||||
{
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "Empty",
|
|
||||||
"diskSizeGB": 32,
|
|
||||||
"lun": 0,
|
|
||||||
"managedDisk": {
|
|
||||||
"storageAccountType": "Standard_LRS"
|
|
||||||
},
|
|
||||||
"name": "[concat(parameters('dataDiskName'),'-1')]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"imageReference": {
|
|
||||||
"offer": "--image-offer--",
|
|
||||||
"publisher": "--image-publisher--",
|
|
||||||
"sku": "--image-sku--",
|
|
||||||
"version": "--version--"
|
|
||||||
},
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"managedDisk": {
|
|
||||||
"storageAccountType": "Standard_LRS"
|
|
||||||
},
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"osType": "Linux"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,230 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminPassword": "[parameters('adminPassword')]",
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"secrets": [
|
|
||||||
{
|
|
||||||
"sourceVault": {
|
|
||||||
"id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', '--keyvault-name--')]"
|
|
||||||
},
|
|
||||||
"vaultCertificates": [
|
|
||||||
{
|
|
||||||
"certificateStore": "My",
|
|
||||||
"certificateUrl": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"windowsConfiguration": {
|
|
||||||
"provisionVMAgent": true,
|
|
||||||
"winRM": {
|
|
||||||
"listeners": [
|
|
||||||
{
|
|
||||||
"certificateUrl": "",
|
|
||||||
"protocol": "https"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"imageReference": {
|
|
||||||
"offer": "--image-offer--",
|
|
||||||
"publisher": "--image-publisher--",
|
|
||||||
"sku": "--image-sku--",
|
|
||||||
"version": "--version--"
|
|
||||||
},
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"managedDisk": {
|
|
||||||
"storageAccountType": "Standard_LRS"
|
|
||||||
},
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"osType": "Windows"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('networkSecurityGroupsApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nsgName')]",
|
|
||||||
"properties": {
|
|
||||||
"securityRules": [
|
|
||||||
{
|
|
||||||
"name": "AllowIPsToSshWinRMInbound",
|
|
||||||
"properties": {
|
|
||||||
"access": "Allow",
|
|
||||||
"description": "Allow inbound traffic from specified IP addresses",
|
|
||||||
"destinationAddressPrefix": "VirtualNetwork",
|
|
||||||
"destinationPortRange": "5985",
|
|
||||||
"direction": "Inbound",
|
|
||||||
"priority": 100,
|
|
||||||
"protocol": "Tcp",
|
|
||||||
"sourceAddressPrefixes": [
|
|
||||||
"127.0.0.1",
|
|
||||||
"192.168.100.0/24"
|
|
||||||
],
|
|
||||||
"sourcePortRange": "*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkSecurityGroups"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkSecurityGroups/', parameters('nsgName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[variables('virtualNetworkName')]",
|
|
||||||
"properties": {
|
|
||||||
"addressSpace": {
|
|
||||||
"addressPrefixes": [
|
|
||||||
"[variables('addressPrefix')]"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"subnets": [
|
|
||||||
{
|
|
||||||
"name": "[variables('subnetName')]",
|
|
||||||
"properties": {
|
|
||||||
"addressPrefix": "[variables('subnetAddressPrefix')]",
|
|
||||||
"networkSecurityGroup": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('nsgName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/virtualNetworks"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,181 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
},
|
|
||||||
"dataDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"dnsNameForPublicIP": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nicName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"nsgName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"osDiskName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"publicIPAddressName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"storageAccountBlobEndpoint": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subnetName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"virtualNetworkName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vmSize": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('publicIPAddressApiVersion')]",
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('publicIPAddressName')]",
|
|
||||||
"properties": {
|
|
||||||
"dnsSettings": {
|
|
||||||
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
|
|
||||||
},
|
|
||||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/publicIPAddresses"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('virtualNetworksApiVersion')]",
|
|
||||||
"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('networkInterfacesApiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]",
|
|
||||||
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('nicName')]",
|
|
||||||
"properties": {
|
|
||||||
"ipConfigurations": [
|
|
||||||
{
|
|
||||||
"name": "ipconfig",
|
|
||||||
"properties": {
|
|
||||||
"privateIPAllocationMethod": "Dynamic",
|
|
||||||
"publicIPAddress": {
|
|
||||||
"id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]"
|
|
||||||
},
|
|
||||||
"subnet": {
|
|
||||||
"id": "[variables('subnetRef')]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Network/networkInterfaces"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"apiVersion": "[variables('apiVersion')]",
|
|
||||||
"dependsOn": [
|
|
||||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]"
|
|
||||||
],
|
|
||||||
"location": "[variables('location')]",
|
|
||||||
"name": "[parameters('vmName')]",
|
|
||||||
"properties": {
|
|
||||||
"diagnosticsProfile": {
|
|
||||||
"bootDiagnostics": {
|
|
||||||
"enabled": true,
|
|
||||||
"storageUri": "https://diagstgaccnt.blob.core.windows.net"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardwareProfile": {
|
|
||||||
"vmSize": "[parameters('vmSize')]"
|
|
||||||
},
|
|
||||||
"networkProfile": {
|
|
||||||
"networkInterfaces": [
|
|
||||||
{
|
|
||||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"osProfile": {
|
|
||||||
"adminUsername": "[parameters('adminUsername')]",
|
|
||||||
"computerName": "[parameters('vmName')]",
|
|
||||||
"linuxConfiguration": {
|
|
||||||
"disablePasswordAuthentication": true,
|
|
||||||
"ssh": {
|
|
||||||
"publicKeys": [
|
|
||||||
{
|
|
||||||
"keyData": "",
|
|
||||||
"path": "[variables('sshKeyPath')]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"imageReference": {
|
|
||||||
"id": ""
|
|
||||||
},
|
|
||||||
"osDisk": {
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"createOption": "FromImage",
|
|
||||||
"managedDisk": {
|
|
||||||
"storageAccountType": "Standard_LRS"
|
|
||||||
},
|
|
||||||
"name": "[parameters('osDiskName')]",
|
|
||||||
"osType": "Linux"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "Microsoft.Compute/virtualMachines"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variables": {
|
|
||||||
"addressPrefix": "10.0.0.0/16",
|
|
||||||
"apiVersion": "2017-03-30",
|
|
||||||
"location": "[resourceGroup().location]",
|
|
||||||
"managedDiskApiVersion": "2017-03-30",
|
|
||||||
"networkInterfacesApiVersion": "2017-04-01",
|
|
||||||
"networkSecurityGroupsApiVersion": "2019-04-01",
|
|
||||||
"publicIPAddressApiVersion": "2017-04-01",
|
|
||||||
"publicIPAddressType": "Dynamic",
|
|
||||||
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
|
|
||||||
"subnetAddressPrefix": "10.0.0.0/24",
|
|
||||||
"subnetName": "[parameters('subnetName')]",
|
|
||||||
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
|
|
||||||
"virtualNetworkName": "[parameters('virtualNetworkName')]",
|
|
||||||
"virtualNetworkResourceGroup": "[resourceGroup().name]",
|
|
||||||
"virtualNetworksApiVersion": "2017-04-01",
|
|
||||||
"vmStorageAccountContainerName": "images",
|
|
||||||
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,659 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources"
|
|
||||||
approvaltests "github.com/approvals/go-approval-tests"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Ensure the link values are not set, and the concrete values are set.
|
|
||||||
func TestVirtualMachineDeployment00(t *testing.T) {
|
|
||||||
var c Config
|
|
||||||
c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration())
|
|
||||||
deployment, err := GetVirtualMachineDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if deployment.Properties.Mode != resources.Incremental {
|
|
||||||
t.Errorf("Expected deployment.Properties.Mode to be %s, but got %s", resources.Incremental, deployment.Properties.Mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
if deployment.Properties.ParametersLink != nil {
|
|
||||||
t.Error("Expected the ParametersLink to be nil!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if deployment.Properties.TemplateLink != nil {
|
|
||||||
t.Error("Expected the TemplateLink to be nil!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if deployment.Properties.Parameters == nil {
|
|
||||||
t.Error("Expected the Parameters to not be nil!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if deployment.Properties.Template == nil {
|
|
||||||
t.Error("Expected the Template to not be nil!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the Virtual Machine template is a valid JSON document.
|
|
||||||
func TestVirtualMachineDeployment01(t *testing.T) {
|
|
||||||
var c Config
|
|
||||||
c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration())
|
|
||||||
deployment, err := GetVirtualMachineDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = json.Marshal(deployment.Properties.Template)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the Virtual Machine template parameters are correct.
|
|
||||||
func TestVirtualMachineDeployment02(t *testing.T) {
|
|
||||||
var c Config
|
|
||||||
c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration())
|
|
||||||
deployment, err := GetVirtualMachineDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bs, err := json.Marshal(deployment.Properties.Parameters)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var params template.TemplateParameters
|
|
||||||
err = json.Unmarshal(bs, ¶ms)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if params.AdminUsername.Value != c.UserName {
|
|
||||||
t.Errorf("Expected template parameter 'AdminUsername' to be %s, but got %s.", params.AdminUsername.Value, c.UserName)
|
|
||||||
}
|
|
||||||
if params.AdminPassword.Value != c.tmpAdminPassword {
|
|
||||||
t.Errorf("Expected template parameter 'AdminPassword' to be %s, but got %s.", params.AdminPassword.Value, c.tmpAdminPassword)
|
|
||||||
}
|
|
||||||
if params.DnsNameForPublicIP.Value != c.tmpComputeName {
|
|
||||||
t.Errorf("Expected template parameter 'DnsNameForPublicIP' to be %s, but got %s.", params.DnsNameForPublicIP.Value, c.tmpComputeName)
|
|
||||||
}
|
|
||||||
if params.OSDiskName.Value != c.tmpOSDiskName {
|
|
||||||
t.Errorf("Expected template parameter 'OSDiskName' to be %s, but got %s.", params.OSDiskName.Value, c.tmpOSDiskName)
|
|
||||||
}
|
|
||||||
if params.StorageAccountBlobEndpoint.Value != c.storageAccountBlobEndpoint {
|
|
||||||
t.Errorf("Expected template parameter 'StorageAccountBlobEndpoint' to be %s, but got %s.", params.StorageAccountBlobEndpoint.Value, c.storageAccountBlobEndpoint)
|
|
||||||
}
|
|
||||||
if params.VMSize.Value != c.VMSize {
|
|
||||||
t.Errorf("Expected template parameter 'VMSize' to be %s, but got %s.", params.VMSize.Value, c.VMSize)
|
|
||||||
}
|
|
||||||
if params.VMName.Value != c.tmpComputeName {
|
|
||||||
t.Errorf("Expected template parameter 'VMName' to be %s, but got %s.", params.VMName.Value, c.tmpComputeName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the VM template is correct when using a market place image.
|
|
||||||
func TestVirtualMachineDeployment03(t *testing.T) {
|
|
||||||
m := getArmBuilderConfiguration()
|
|
||||||
m["image_publisher"] = "ImagePublisher"
|
|
||||||
m["image_offer"] = "ImageOffer"
|
|
||||||
m["image_sku"] = "ImageSku"
|
|
||||||
m["image_version"] = "ImageVersion"
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(m, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
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 VM template is correct when using a custom image.
|
|
||||||
func TestVirtualMachineDeployment04(t *testing.T) {
|
|
||||||
config := map[string]string{
|
|
||||||
"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",
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(config, getPackerConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
deployment, err := GetVirtualMachineDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVirtualMachineDeployment05(t *testing.T) {
|
|
||||||
config := map[string]string{
|
|
||||||
"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",
|
|
||||||
"virtual_network_name": "virtualNetworkName",
|
|
||||||
"virtual_network_resource_group_name": "virtualNetworkResourceGroupName",
|
|
||||||
"virtual_network_subnet_name": "virtualNetworkSubnetName",
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(config, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
deployment, err := GetVirtualMachineDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that tags are properly applied to every resource
|
|
||||||
func TestVirtualMachineDeployment06(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",
|
|
||||||
"azure_tags": map[string]string{
|
|
||||||
"tag01": "value01",
|
|
||||||
"tag02": "value02",
|
|
||||||
"tag03": "value03",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(config, getPackerConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
deployment, err := GetVirtualMachineDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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",
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(config, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration())
|
|
||||||
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 VM template is correct when building from a custom managed image.
|
|
||||||
func TestVirtualMachineDeployment08(t *testing.T) {
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"location": "ignore",
|
|
||||||
"subscription_id": "ignore",
|
|
||||||
"os_type": constants.Target_Linux,
|
|
||||||
"communicator": "none",
|
|
||||||
"custom_managed_image_resource_group_name": "CustomManagedImageResourceGroupName",
|
|
||||||
"custom_managed_image_name": "CustomManagedImageName",
|
|
||||||
"managed_image_name": "ManagedImageName",
|
|
||||||
"managed_image_resource_group_name": "ManagedImageResourceGroupName",
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(config, getPackerConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 VM template is correct when building from a platform managed image.
|
|
||||||
func TestVirtualMachineDeployment09(t *testing.T) {
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"location": "ignore",
|
|
||||||
"subscription_id": "ignore",
|
|
||||||
"os_type": constants.Target_Linux,
|
|
||||||
"communicator": "none",
|
|
||||||
"image_publisher": "--image-publisher--",
|
|
||||||
"image_offer": "--image-offer--",
|
|
||||||
"image_sku": "--image-sku--",
|
|
||||||
"image_version": "--version--",
|
|
||||||
"managed_image_name": "ManagedImageName",
|
|
||||||
"managed_image_resource_group_name": "ManagedImageResourceGroupName",
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(config, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 VM template is correct when building with PublicIp and connect to Private Network
|
|
||||||
func TestVirtualMachineDeployment10(t *testing.T) {
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"location": "ignore",
|
|
||||||
"subscription_id": "ignore",
|
|
||||||
"os_type": constants.Target_Linux,
|
|
||||||
"communicator": "none",
|
|
||||||
"image_publisher": "--image-publisher--",
|
|
||||||
"image_offer": "--image-offer--",
|
|
||||||
"image_sku": "--image-sku--",
|
|
||||||
"image_version": "--version--",
|
|
||||||
|
|
||||||
"virtual_network_resource_group_name": "--virtual_network_resource_group_name--",
|
|
||||||
"virtual_network_name": "--virtual_network_name--",
|
|
||||||
"virtual_network_subnet_name": "--virtual_network_subnet_name--",
|
|
||||||
"private_virtual_network_with_public_ip": true,
|
|
||||||
|
|
||||||
"managed_image_name": "ManagedImageName",
|
|
||||||
"managed_image_resource_group_name": "ManagedImageResourceGroupName",
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(config, getPackerConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 VM template is correct when building with additional unmanaged disks
|
|
||||||
func TestVirtualMachineDeployment11(t *testing.T) {
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"location": "ignore",
|
|
||||||
"subscription_id": "ignore",
|
|
||||||
"os_type": constants.Target_Linux,
|
|
||||||
"communicator": "none",
|
|
||||||
"image_publisher": "--image-publisher--",
|
|
||||||
"image_offer": "--image-offer--",
|
|
||||||
"image_sku": "--image-sku--",
|
|
||||||
"image_version": "--version--",
|
|
||||||
|
|
||||||
"disk_additional_size": []uint{32},
|
|
||||||
|
|
||||||
"resource_group_name": "packergroup",
|
|
||||||
"storage_account": "packerartifacts",
|
|
||||||
"capture_name_prefix": "packer",
|
|
||||||
"capture_container_name": "packerimages",
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(config, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 VM template is correct when building with additional managed disks
|
|
||||||
func TestVirtualMachineDeployment12(t *testing.T) {
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"location": "ignore",
|
|
||||||
"subscription_id": "ignore",
|
|
||||||
"os_type": constants.Target_Linux,
|
|
||||||
"communicator": "none",
|
|
||||||
"image_publisher": "--image-publisher--",
|
|
||||||
"image_offer": "--image-offer--",
|
|
||||||
"image_sku": "--image-sku--",
|
|
||||||
"image_version": "--version--",
|
|
||||||
|
|
||||||
"disk_additional_size": []uint{32},
|
|
||||||
|
|
||||||
"managed_image_name": "ManagedImageName",
|
|
||||||
"managed_image_resource_group_name": "ManagedImageResourceGroupName",
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(config, getPackerConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 VM template is correct when building with list of allowed IP addresses
|
|
||||||
func TestVirtualMachineDeployment13(t *testing.T) {
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"location": "ignore",
|
|
||||||
"subscription_id": "ignore",
|
|
||||||
"os_type": constants.Target_Windows,
|
|
||||||
"communicator": "winrm",
|
|
||||||
"winrm_username": "ignore",
|
|
||||||
"image_publisher": "--image-publisher--",
|
|
||||||
"image_offer": "--image-offer--",
|
|
||||||
"image_sku": "--image-sku--",
|
|
||||||
"image_version": "--version--",
|
|
||||||
"managed_image_name": "ManagedImageName",
|
|
||||||
"managed_image_resource_group_name": "ManagedImageResourceGroupName",
|
|
||||||
"allowed_inbound_ip_addresses": []string{"127.0.0.1", "192.168.100.0/24"},
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(config, getPackerConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
c.tmpKeyVaultName = "--keyvault-name--"
|
|
||||||
|
|
||||||
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 VM template is correct when building with bootdiagnostics
|
|
||||||
func TestVirtualMachineDeployment14(t *testing.T) {
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"location": "ignore",
|
|
||||||
"subscription_id": "ignore",
|
|
||||||
"os_type": constants.Target_Linux,
|
|
||||||
"communicator": "none",
|
|
||||||
"custom_managed_image_resource_group_name": "CustomManagedImageResourceGroupName",
|
|
||||||
"custom_managed_image_name": "CustomManagedImageName",
|
|
||||||
"managed_image_name": "ManagedImageName",
|
|
||||||
"managed_image_resource_group_name": "ManagedImageResourceGroupName",
|
|
||||||
"boot_diag_storage_account": "diagstgaccnt",
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
_, err := c.Prepare(config, getPackerConfiguration())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
var c Config
|
|
||||||
c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration())
|
|
||||||
deployment, err := GetKeyVaultDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if deployment.Properties.Mode != resources.Incremental {
|
|
||||||
t.Errorf("Expected deployment.Properties.Mode to be %s, but got %s", resources.Incremental, deployment.Properties.Mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
if deployment.Properties.ParametersLink != nil {
|
|
||||||
t.Error("Expected the ParametersLink to be nil!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if deployment.Properties.TemplateLink != nil {
|
|
||||||
t.Error("Expected the TemplateLink to be nil!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if deployment.Properties.Parameters == nil {
|
|
||||||
t.Error("Expected the Parameters to not be nil!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if deployment.Properties.Template == nil {
|
|
||||||
t.Error("Expected the Template to not be nil!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the KeyVault template is a valid JSON document.
|
|
||||||
func TestKeyVaultDeployment01(t *testing.T) {
|
|
||||||
var c Config
|
|
||||||
c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration())
|
|
||||||
deployment, err := GetKeyVaultDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = json.Marshal(deployment.Properties.Template)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the KeyVault template parameters are correct.
|
|
||||||
func TestKeyVaultDeployment02(t *testing.T) {
|
|
||||||
var c Config
|
|
||||||
c.Prepare(getArmBuilderConfigurationWithWindows(), getPackerConfiguration())
|
|
||||||
|
|
||||||
deployment, err := GetKeyVaultDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bs, err := json.Marshal(deployment.Properties.Parameters)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var params template.TemplateParameters
|
|
||||||
err = json.Unmarshal(bs, ¶ms)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if params.ObjectId.Value != c.ClientConfig.ObjectID {
|
|
||||||
t.Errorf("Expected template parameter 'ObjectId' to be %s, but got %s.", params.ObjectId.Value, c.ClientConfig.ObjectID)
|
|
||||||
}
|
|
||||||
if params.TenantId.Value != c.ClientConfig.TenantID {
|
|
||||||
t.Errorf("Expected template parameter 'TenantId' to be %s, but got %s.", params.TenantId.Value, c.ClientConfig.TenantID)
|
|
||||||
}
|
|
||||||
if params.KeyVaultName.Value != c.tmpKeyVaultName {
|
|
||||||
t.Errorf("Expected template parameter 'KeyVaultName' to be %s, but got %s.", params.KeyVaultName.Value, c.tmpKeyVaultName)
|
|
||||||
}
|
|
||||||
if params.KeyVaultSecretValue.Value != c.winrmCertificate {
|
|
||||||
t.Errorf("Expected template parameter 'KeyVaultSecretValue' to be %s, but got %s.", params.KeyVaultSecretValue.Value, c.winrmCertificate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the KeyVault template is correct when tags are supplied.
|
|
||||||
func TestKeyVaultDeployment03(t *testing.T) {
|
|
||||||
tags := map[string]interface{}{
|
|
||||||
"azure_tags": map[string]string{
|
|
||||||
"tag01": "value01",
|
|
||||||
"tag02": "value02",
|
|
||||||
"tag03": "value03",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
c.Prepare(tags, getArmBuilderConfigurationWithWindows(), getPackerConfiguration())
|
|
||||||
deployment, err := GetKeyVaultDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPlanInfo01(t *testing.T) {
|
|
||||||
planInfo := map[string]interface{}{
|
|
||||||
"plan_info": map[string]string{
|
|
||||||
"plan_name": "planName00",
|
|
||||||
"plan_product": "planProduct00",
|
|
||||||
"plan_publisher": "planPublisher00",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
c.Prepare(planInfo, getArmBuilderConfiguration(), getPackerConfiguration())
|
|
||||||
deployment, err := GetVirtualMachineDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPlanInfo02(t *testing.T) {
|
|
||||||
planInfo := map[string]interface{}{
|
|
||||||
"azure_tags": map[string]string{
|
|
||||||
"dept": "engineering",
|
|
||||||
},
|
|
||||||
"plan_info": map[string]string{
|
|
||||||
"plan_name": "planName00",
|
|
||||||
"plan_product": "planProduct00",
|
|
||||||
"plan_publisher": "planPublisher00",
|
|
||||||
"plan_promotion_code": "planPromotionCode00",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var c Config
|
|
||||||
c.Prepare(planInfo, getArmBuilderConfiguration(), getPackerConfiguration())
|
|
||||||
deployment, err := GetVirtualMachineDeployment(&c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/random"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TempName struct {
|
|
||||||
AdminPassword string
|
|
||||||
CertificatePassword string
|
|
||||||
ComputeName string
|
|
||||||
DeploymentName string
|
|
||||||
KeyVaultName string
|
|
||||||
ResourceGroupName string
|
|
||||||
OSDiskName string
|
|
||||||
DataDiskName string
|
|
||||||
NicName string
|
|
||||||
SubnetName string
|
|
||||||
PublicIPAddressName string
|
|
||||||
VirtualNetworkName string
|
|
||||||
NsgName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTempName(p string) *TempName {
|
|
||||||
tempName := &TempName{}
|
|
||||||
|
|
||||||
suffix := random.AlphaNumLower(5)
|
|
||||||
if p == "" {
|
|
||||||
p = "pkr"
|
|
||||||
suffix = random.AlphaNumLower(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
tempName.ComputeName = fmt.Sprintf("%svm%s", p, suffix)
|
|
||||||
tempName.DeploymentName = fmt.Sprintf("%sdp%s", p, suffix)
|
|
||||||
tempName.KeyVaultName = fmt.Sprintf("%skv%s", p, suffix)
|
|
||||||
tempName.OSDiskName = fmt.Sprintf("%sos%s", p, suffix)
|
|
||||||
tempName.DataDiskName = fmt.Sprintf("%sdd%s", p, suffix)
|
|
||||||
tempName.NicName = fmt.Sprintf("%sni%s", p, suffix)
|
|
||||||
tempName.PublicIPAddressName = fmt.Sprintf("%sip%s", p, suffix)
|
|
||||||
tempName.SubnetName = fmt.Sprintf("%ssn%s", p, suffix)
|
|
||||||
tempName.VirtualNetworkName = fmt.Sprintf("%svn%s", p, suffix)
|
|
||||||
tempName.NsgName = fmt.Sprintf("%ssg%s", p, suffix)
|
|
||||||
tempName.ResourceGroupName = fmt.Sprintf("%s-Resource-Group-%s", p, suffix)
|
|
||||||
|
|
||||||
tempName.AdminPassword = generatePassword()
|
|
||||||
tempName.CertificatePassword = random.AlphaNum(32)
|
|
||||||
|
|
||||||
return tempName
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate a password that is acceptable to Azure
|
|
||||||
// Three of the four items must be met.
|
|
||||||
// 1. Contains an uppercase character
|
|
||||||
// 2. Contains a lowercase character
|
|
||||||
// 3. Contains a numeric digit
|
|
||||||
// 4. Contains a special character
|
|
||||||
func generatePassword() string {
|
|
||||||
var s string
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
s := random.AlphaNum(32)
|
|
||||||
if !strings.ContainsAny(s, random.PossibleNumbers) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.ContainsAny(s, random.PossibleLowerCase) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.ContainsAny(s, random.PossibleUpperCase) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// if an acceptable password cannot be generated in 100 tries, give up
|
|
||||||
return s
|
|
||||||
}
|
|
|
@ -1,143 +0,0 @@
|
||||||
package arm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/random"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTempNameShouldCreatePrefixedRandomNames(t *testing.T) {
|
|
||||||
tempName := NewTempName("")
|
|
||||||
|
|
||||||
if strings.Index(tempName.ComputeName, "pkrvm") != 0 {
|
|
||||||
t.Errorf("Expected ComputeName to begin with 'pkrvm', but got '%s'!", tempName.ComputeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.DeploymentName, "pkrdp") != 0 {
|
|
||||||
t.Errorf("Expected ComputeName to begin with 'pkrdp', but got '%s'!", tempName.ComputeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.OSDiskName, "pkros") != 0 {
|
|
||||||
t.Errorf("Expected OSDiskName to begin with 'pkros', but got '%s'!", tempName.OSDiskName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.NicName, "pkrni") != 0 {
|
|
||||||
t.Errorf("Expected NicName to begin with 'pkrni', but got '%s'!", tempName.NicName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.PublicIPAddressName, "pkrip") != 0 {
|
|
||||||
t.Errorf("Expected PublicIPAddressName to begin with 'pkrip', but got '%s'!", tempName.PublicIPAddressName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.ResourceGroupName, "pkr-Resource-Group-") != 0 {
|
|
||||||
t.Errorf("Expected ResourceGroupName to begin with 'pkr-Resource-Group-', but got '%s'!", tempName.ResourceGroupName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.SubnetName, "pkrsn") != 0 {
|
|
||||||
t.Errorf("Expected SubnetName to begin with 'pkrip', but got '%s'!", tempName.SubnetName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.VirtualNetworkName, "pkrvn") != 0 {
|
|
||||||
t.Errorf("Expected VirtualNetworkName to begin with 'pkrvn', but got '%s'!", tempName.VirtualNetworkName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.NsgName, "pkrsg") != 0 {
|
|
||||||
t.Errorf("Expected NsgName to begin with 'pkrsg', but got '%s'!", tempName.NsgName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTempAdminPassword(t *testing.T) {
|
|
||||||
tempName := NewTempName("")
|
|
||||||
|
|
||||||
if !strings.ContainsAny(tempName.AdminPassword, random.PossibleNumbers) {
|
|
||||||
t.Errorf("Expected AdminPassword to contain at least one of '%s'!", random.PossibleNumbers)
|
|
||||||
}
|
|
||||||
if !strings.ContainsAny(tempName.AdminPassword, random.PossibleLowerCase) {
|
|
||||||
t.Errorf("Expected AdminPassword to contain at least one of '%s'!", random.PossibleLowerCase)
|
|
||||||
}
|
|
||||||
if !strings.ContainsAny(tempName.AdminPassword, random.PossibleUpperCase) {
|
|
||||||
t.Errorf("Expected AdminPassword to contain at least one of '%s'!", random.PossibleUpperCase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTempNameShouldHaveSameSuffix(t *testing.T) {
|
|
||||||
tempName := NewTempName("")
|
|
||||||
suffix := tempName.ComputeName[5:]
|
|
||||||
|
|
||||||
if strings.HasSuffix(tempName.ComputeName, suffix) != true {
|
|
||||||
t.Errorf("Expected ComputeName to end with '%s', but the value is '%s'!", suffix, tempName.ComputeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(tempName.DeploymentName, suffix) != true {
|
|
||||||
t.Errorf("Expected DeploymentName to end with '%s', but the value is '%s'!", suffix, tempName.DeploymentName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(tempName.OSDiskName, suffix) != true {
|
|
||||||
t.Errorf("Expected OSDiskName to end with '%s', but the value is '%s'!", suffix, tempName.OSDiskName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(tempName.NicName, suffix) != true {
|
|
||||||
t.Errorf("Expected NicName to end with '%s', but the value is '%s'!", suffix, tempName.PublicIPAddressName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(tempName.PublicIPAddressName, suffix) != true {
|
|
||||||
t.Errorf("Expected PublicIPAddressName to end with '%s', but the value is '%s'!", suffix, tempName.PublicIPAddressName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(tempName.ResourceGroupName, suffix) != true {
|
|
||||||
t.Errorf("Expected ResourceGroupName to end with '%s', but the value is '%s'!", suffix, tempName.ResourceGroupName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(tempName.SubnetName, suffix) != true {
|
|
||||||
t.Errorf("Expected SubnetName to end with '%s', but the value is '%s'!", suffix, tempName.SubnetName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(tempName.VirtualNetworkName, suffix) != true {
|
|
||||||
t.Errorf("Expected VirtualNetworkName to end with '%s', but the value is '%s'!", suffix, tempName.VirtualNetworkName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(tempName.NsgName, suffix) != true {
|
|
||||||
t.Errorf("Expected NsgName to end with '%s', but the value is '%s'!", suffix, tempName.NsgName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTempNameShouldCreateCustomPrefix(t *testing.T) {
|
|
||||||
tempName := NewTempName("CustPrefix")
|
|
||||||
|
|
||||||
if strings.Index(tempName.ComputeName, "CustPrefixvm") != 0 {
|
|
||||||
t.Errorf("Expected ComputeName to begin with 'CustPrefixvm', but got '%s'!", tempName.ComputeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.DeploymentName, "CustPrefixdp") != 0 {
|
|
||||||
t.Errorf("Expected ComputeName to begin with 'CustPrefixdp', but got '%s'!", tempName.ComputeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.OSDiskName, "CustPrefixos") != 0 {
|
|
||||||
t.Errorf("Expected OSDiskName to begin with 'CustPrefixos', but got '%s'!", tempName.OSDiskName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.NicName, "CustPrefixni") != 0 {
|
|
||||||
t.Errorf("Expected NicName to begin with 'CustPrefixni', but got '%s'!", tempName.NicName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.PublicIPAddressName, "CustPrefixip") != 0 {
|
|
||||||
t.Errorf("Expected PublicIPAddressName to begin with 'CustPrefixip', but got '%s'!", tempName.PublicIPAddressName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.ResourceGroupName, "CustPrefix-Resource-Group-") != 0 {
|
|
||||||
t.Errorf("Expected ResourceGroupName to begin with 'packer-Resource-Group-', but got '%s'!", tempName.ResourceGroupName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.SubnetName, "CustPrefixsn") != 0 {
|
|
||||||
t.Errorf("Expected SubnetName to begin with 'pkrip', but got '%s'!", tempName.SubnetName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.VirtualNetworkName, "CustPrefixvn") != 0 {
|
|
||||||
t.Errorf("Expected VirtualNetworkName to begin with 'CustPrefixvn', but got '%s'!", tempName.VirtualNetworkName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(tempName.NsgName, "CustPrefixsg") != 0 {
|
|
||||||
t.Errorf("Expected NsgName to begin with 'CustPrefixsg', but got '%s'!", tempName.NsgName)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,622 +0,0 @@
|
||||||
//go:generate packer-sdc struct-markdown
|
|
||||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
|
||||||
|
|
||||||
// Package chroot is able to create an Azure managed image without requiring the
|
|
||||||
// launch of a new virtual machine for every build. It does this by attaching and
|
|
||||||
// mounting the root disk and chrooting into that directory.
|
|
||||||
// It then creates a managed image from that attached disk.
|
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/chroot"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
|
||||||
azcommon "github.com/hashicorp/packer/builder/azure/common"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BuilderID is the unique ID for this builder
|
|
||||||
const BuilderID = "azure.chroot"
|
|
||||||
|
|
||||||
// Config is the configuration that is chained through the steps and settable
|
|
||||||
// from the template.
|
|
||||||
type Config struct {
|
|
||||||
common.PackerConfig `mapstructure:",squash"`
|
|
||||||
|
|
||||||
ClientConfig client.Config `mapstructure:",squash"`
|
|
||||||
|
|
||||||
// When set to `true`, starts with an empty, unpartitioned disk. Defaults to `false`.
|
|
||||||
FromScratch bool `mapstructure:"from_scratch"`
|
|
||||||
// One of the following can be used as a source for an image:
|
|
||||||
// - a shared image version resource ID
|
|
||||||
// - a managed disk resource ID
|
|
||||||
// - a publisher:offer:sku:version specifier for plaform image sources.
|
|
||||||
Source string `mapstructure:"source" required:"true"`
|
|
||||||
sourceType sourceType
|
|
||||||
|
|
||||||
// How to run shell commands. This may be useful to set environment variables or perhaps run
|
|
||||||
// a command with sudo or so on. This is a configuration template where the `.Command` variable
|
|
||||||
// is replaced with the command to be run. Defaults to `{{.Command}}`.
|
|
||||||
CommandWrapper string `mapstructure:"command_wrapper"`
|
|
||||||
// A series of commands to execute after attaching the root volume and before mounting the chroot.
|
|
||||||
// This is not required unless using `from_scratch`. If so, this should include any partitioning
|
|
||||||
// and filesystem creation commands. The path to the device is provided by `{{.Device}}`.
|
|
||||||
PreMountCommands []string `mapstructure:"pre_mount_commands"`
|
|
||||||
// Options to supply the `mount` command when mounting devices. Each option will be prefixed with
|
|
||||||
// `-o` and supplied to the `mount` command ran by Packer. Because this command is ran in a shell,
|
|
||||||
// user discretion is advised. See this manual page for the `mount` command for valid file system specific options.
|
|
||||||
MountOptions []string `mapstructure:"mount_options"`
|
|
||||||
// The partition number containing the / partition. By default this is the first partition of the volume.
|
|
||||||
MountPartition string `mapstructure:"mount_partition"`
|
|
||||||
// The path where the volume will be mounted. This is where the chroot environment will be. This defaults
|
|
||||||
// to `/mnt/packer-amazon-chroot-volumes/{{.Device}}`. This is a configuration template where the `.Device`
|
|
||||||
// variable is replaced with the name of the device where the volume is attached.
|
|
||||||
MountPath string `mapstructure:"mount_path"`
|
|
||||||
// As `pre_mount_commands`, but the commands are executed after mounting the root device and before the
|
|
||||||
// extra mount and copy steps. The device and mount path are provided by `{{.Device}}` and `{{.MountPath}}`.
|
|
||||||
PostMountCommands []string `mapstructure:"post_mount_commands"`
|
|
||||||
// This is a list of devices to mount into the chroot environment. This configuration parameter requires
|
|
||||||
// some additional documentation which is in the "Chroot Mounts" section below. Please read that section
|
|
||||||
// for more information on how to use this.
|
|
||||||
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
|
|
||||||
// Paths to files on the running Azure instance that will be copied into the chroot environment prior to
|
|
||||||
// provisioning. Defaults to `/etc/resolv.conf` so that DNS lookups work. Pass an empty list to skip copying
|
|
||||||
// `/etc/resolv.conf`. You may need to do this if you're building an image that uses systemd.
|
|
||||||
CopyFiles []string `mapstructure:"copy_files"`
|
|
||||||
|
|
||||||
// Try to resize the OS disk to this size on the first copy. Disks can only be englarged. If not specified,
|
|
||||||
// the disk will keep its original size. Required when using `from_scratch`
|
|
||||||
OSDiskSizeGB int32 `mapstructure:"os_disk_size_gb"`
|
|
||||||
// The [storage SKU](https://docs.microsoft.com/en-us/rest/api/compute/disks/createorupdate#diskstorageaccounttypes)
|
|
||||||
// to use for the OS Disk. Defaults to `Standard_LRS`.
|
|
||||||
OSDiskStorageAccountType string `mapstructure:"os_disk_storage_account_type"`
|
|
||||||
// The [cache type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#cachingtypes)
|
|
||||||
// specified in the resulting image and for attaching it to the Packer VM. Defaults to `ReadOnly`
|
|
||||||
OSDiskCacheType string `mapstructure:"os_disk_cache_type"`
|
|
||||||
|
|
||||||
// The [storage SKU](https://docs.microsoft.com/en-us/rest/api/compute/disks/createorupdate#diskstorageaccounttypes)
|
|
||||||
// to use for datadisks. Defaults to `Standard_LRS`.
|
|
||||||
DataDiskStorageAccountType string `mapstructure:"data_disk_storage_account_type"`
|
|
||||||
// The [cache type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#cachingtypes)
|
|
||||||
// specified in the resulting image and for attaching it to the Packer VM. Defaults to `ReadOnly`
|
|
||||||
DataDiskCacheType string `mapstructure:"data_disk_cache_type"`
|
|
||||||
|
|
||||||
// The [Hyper-V generation type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#hypervgenerationtypes) for Managed Image output.
|
|
||||||
// Defaults to `V1`.
|
|
||||||
ImageHyperVGeneration string `mapstructure:"image_hyperv_generation"`
|
|
||||||
|
|
||||||
// The id of the temporary OS disk that will be created. Will be generated if not set.
|
|
||||||
TemporaryOSDiskID string `mapstructure:"temporary_os_disk_id"`
|
|
||||||
|
|
||||||
// The id of the temporary OS disk snapshot that will be created. Will be generated if not set.
|
|
||||||
TemporaryOSDiskSnapshotID string `mapstructure:"temporary_os_disk_snapshot_id"`
|
|
||||||
|
|
||||||
// The prefix for the resource ids of the temporary data disks that will be created. The disks will be suffixed with a number. Will be generated if not set.
|
|
||||||
TemporaryDataDiskIDPrefix string `mapstructure:"temporary_data_disk_id_prefix"`
|
|
||||||
|
|
||||||
// The prefix for the resource ids of the temporary data disk snapshots that will be created. The snapshots will be suffixed with a number. Will be generated if not set.
|
|
||||||
TemporaryDataDiskSnapshotIDPrefix string `mapstructure:"temporary_data_disk_snapshot_id"`
|
|
||||||
|
|
||||||
// If set to `true`, leaves the temporary disks and snapshots behind in the Packer VM resource group. Defaults to `false`
|
|
||||||
SkipCleanup bool `mapstructure:"skip_cleanup"`
|
|
||||||
|
|
||||||
// The managed image to create using this build.
|
|
||||||
ImageResourceID string `mapstructure:"image_resource_id"`
|
|
||||||
|
|
||||||
// The shared image to create using this build.
|
|
||||||
SharedImageGalleryDestination SharedImageGalleryDestination `mapstructure:"shared_image_destination"`
|
|
||||||
|
|
||||||
ctx interpolate.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
type sourceType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
sourcePlatformImage sourceType = "PlatformImage"
|
|
||||||
sourceDisk sourceType = "Disk"
|
|
||||||
sourceSharedImage sourceType = "SharedImage"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetContext implements ContextProvider to allow steps to use the config context
|
|
||||||
// for template interpolation
|
|
||||||
func (c *Config) GetContext() interpolate.Context {
|
|
||||||
return c.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
type Builder struct {
|
|
||||||
config Config
|
|
||||||
runner multistep.Runner
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify interface implementation
|
|
||||||
var _ packersdk.Builder = &Builder{}
|
|
||||||
|
|
||||||
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
|
|
||||||
|
|
||||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
|
||||||
b.config.ctx.Funcs = azcommon.TemplateFuncs
|
|
||||||
b.config.ctx.Funcs["vm"] = CreateVMMetadataTemplateFunc()
|
|
||||||
md := &mapstructure.Metadata{}
|
|
||||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
|
||||||
PluginType: BuilderID,
|
|
||||||
Interpolate: true,
|
|
||||||
InterpolateContext: &b.config.ctx,
|
|
||||||
InterpolateFilter: &interpolate.RenderFilter{
|
|
||||||
Exclude: []string{
|
|
||||||
// these fields are interpolated in the steps,
|
|
||||||
// when more information is available
|
|
||||||
"command_wrapper",
|
|
||||||
"post_mount_commands",
|
|
||||||
"pre_mount_commands",
|
|
||||||
"mount_path",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Metadata: md,
|
|
||||||
}, raws...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var errs *packersdk.MultiError
|
|
||||||
var warns []string
|
|
||||||
|
|
||||||
// Defaults
|
|
||||||
err = b.config.ClientConfig.SetDefaultValues()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.ChrootMounts == nil {
|
|
||||||
b.config.ChrootMounts = make([][]string, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b.config.ChrootMounts) == 0 {
|
|
||||||
b.config.ChrootMounts = [][]string{
|
|
||||||
{"proc", "proc", "/proc"},
|
|
||||||
{"sysfs", "sysfs", "/sys"},
|
|
||||||
{"bind", "/dev", "/dev"},
|
|
||||||
{"devpts", "devpts", "/dev/pts"},
|
|
||||||
{"binfmt_misc", "binfmt_misc", "/proc/sys/fs/binfmt_misc"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set default copy file if we're not giving our own
|
|
||||||
if b.config.CopyFiles == nil {
|
|
||||||
if !b.config.FromScratch {
|
|
||||||
b.config.CopyFiles = []string{"/etc/resolv.conf"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.CommandWrapper == "" {
|
|
||||||
b.config.CommandWrapper = "{{.Command}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.MountPath == "" {
|
|
||||||
b.config.MountPath = "/mnt/packer-azure-chroot-disks/{{.Device}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.MountPartition == "" {
|
|
||||||
b.config.MountPartition = "1"
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.TemporaryOSDiskID == "" {
|
|
||||||
if def, err := interpolate.Render(
|
|
||||||
"/subscriptions/{{ vm `subscription_id` }}/resourceGroups/{{ vm `resource_group` }}/providers/Microsoft.Compute/disks/PackerTemp-osdisk-{{timestamp}}",
|
|
||||||
&b.config.ctx); err == nil {
|
|
||||||
b.config.TemporaryOSDiskID = def
|
|
||||||
} else {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("unable to render temporary disk id: %s", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.TemporaryOSDiskSnapshotID == "" {
|
|
||||||
if def, err := interpolate.Render(
|
|
||||||
"/subscriptions/{{ vm `subscription_id` }}/resourceGroups/{{ vm `resource_group` }}/providers/Microsoft.Compute/snapshots/PackerTemp-osdisk-snapshot-{{timestamp}}",
|
|
||||||
&b.config.ctx); err == nil {
|
|
||||||
b.config.TemporaryOSDiskSnapshotID = def
|
|
||||||
} else {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("unable to render temporary snapshot id: %s", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.TemporaryDataDiskIDPrefix == "" {
|
|
||||||
if def, err := interpolate.Render(
|
|
||||||
"/subscriptions/{{ vm `subscription_id` }}/resourceGroups/{{ vm `resource_group` }}/providers/Microsoft.Compute/disks/PackerTemp-datadisk-{{timestamp}}-",
|
|
||||||
&b.config.ctx); err == nil {
|
|
||||||
b.config.TemporaryDataDiskIDPrefix = def
|
|
||||||
} else {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("unable to render temporary data disk id prefix: %s", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.TemporaryDataDiskSnapshotIDPrefix == "" {
|
|
||||||
if def, err := interpolate.Render(
|
|
||||||
"/subscriptions/{{ vm `subscription_id` }}/resourceGroups/{{ vm `resource_group` }}/providers/Microsoft.Compute/snapshots/PackerTemp-datadisk-snapshot-{{timestamp}}-",
|
|
||||||
&b.config.ctx); err == nil {
|
|
||||||
b.config.TemporaryDataDiskSnapshotIDPrefix = def
|
|
||||||
} else {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("unable to render temporary data disk snapshot id prefix: %s", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.OSDiskStorageAccountType == "" {
|
|
||||||
b.config.OSDiskStorageAccountType = string(compute.PremiumLRS)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.OSDiskCacheType == "" {
|
|
||||||
b.config.OSDiskCacheType = string(compute.CachingTypesReadOnly)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.DataDiskStorageAccountType == "" {
|
|
||||||
b.config.DataDiskStorageAccountType = string(compute.PremiumLRS)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.DataDiskCacheType == "" {
|
|
||||||
b.config.DataDiskCacheType = string(compute.CachingTypesReadOnly)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.ImageHyperVGeneration == "" {
|
|
||||||
b.config.ImageHyperVGeneration = string(compute.V1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checks, accumulate any errors or warnings
|
|
||||||
|
|
||||||
if b.config.FromScratch {
|
|
||||||
if b.config.Source != "" {
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, errors.New("source cannot be specified when building from_scratch"))
|
|
||||||
}
|
|
||||||
if b.config.OSDiskSizeGB == 0 {
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, errors.New("os_disk_size_gb is required with from_scratch"))
|
|
||||||
}
|
|
||||||
if len(b.config.PreMountCommands) == 0 {
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, errors.New("pre_mount_commands is required with from_scratch"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if _, err := client.ParsePlatformImageURN(b.config.Source); err == nil {
|
|
||||||
log.Println("Source is platform image:", b.config.Source)
|
|
||||||
b.config.sourceType = sourcePlatformImage
|
|
||||||
} else if id, err := client.ParseResourceID(b.config.Source); err == nil &&
|
|
||||||
strings.EqualFold(id.Provider, "Microsoft.Compute") &&
|
|
||||||
strings.EqualFold(id.ResourceType.String(), "disks") {
|
|
||||||
log.Println("Source is a disk resource ID:", b.config.Source)
|
|
||||||
b.config.sourceType = sourceDisk
|
|
||||||
} else if id, err := client.ParseResourceID(b.config.Source); err == nil &&
|
|
||||||
strings.EqualFold(id.Provider, "Microsoft.Compute") &&
|
|
||||||
strings.EqualFold(id.ResourceType.String(), "galleries/images/versions") {
|
|
||||||
log.Println("Source is a shared image ID:", b.config.Source)
|
|
||||||
b.config.sourceType = sourceSharedImage
|
|
||||||
} else {
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, fmt.Errorf("source: %q is not a valid platform image specifier, nor is it a disk resource ID", b.config.Source))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := checkDiskCacheType(b.config.OSDiskCacheType); err != nil {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("os_disk_cache_type: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := checkStorageAccountType(b.config.OSDiskStorageAccountType); err != nil {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("os_disk_storage_account_type: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := checkDiskCacheType(b.config.DataDiskCacheType); err != nil {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("data_disk_cache_type: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := checkStorageAccountType(b.config.DataDiskStorageAccountType); err != nil {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("data_disk_storage_account_type: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.ImageResourceID != "" {
|
|
||||||
r, err := azure.ParseResourceID(b.config.ImageResourceID)
|
|
||||||
if err != nil ||
|
|
||||||
!strings.EqualFold(r.Provider, "Microsoft.Compute") ||
|
|
||||||
!strings.EqualFold(r.ResourceType, "images") {
|
|
||||||
errs = packersdk.MultiErrorAppend(fmt.Errorf(
|
|
||||||
"image_resource_id: %q is not a valid image resource id", b.config.ImageResourceID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if azcommon.StringsContains(md.Keys, "shared_image_destination") {
|
|
||||||
e, w := b.config.SharedImageGalleryDestination.Validate("shared_image_destination")
|
|
||||||
if len(e) > 0 {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, e...)
|
|
||||||
}
|
|
||||||
if len(w) > 0 {
|
|
||||||
warns = append(warns, w...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !azcommon.StringsContains(md.Keys, "shared_image_destination") && b.config.ImageResourceID == "" {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, errors.New("image_resource_id or shared_image_destination is required"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := checkHyperVGeneration(b.config.ImageHyperVGeneration); err != nil {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("image_hyperv_generation: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if errs != nil {
|
|
||||||
return nil, warns, errs
|
|
||||||
}
|
|
||||||
|
|
||||||
packersdk.LogSecretFilter.Set(b.config.ClientConfig.ClientSecret, b.config.ClientConfig.ClientJWT)
|
|
||||||
return nil, warns, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkDiskCacheType(s string) interface{} {
|
|
||||||
for _, v := range compute.PossibleCachingTypesValues() {
|
|
||||||
if compute.CachingTypes(s) == v {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("%q is not a valid value %v",
|
|
||||||
s, compute.PossibleCachingTypesValues())
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkStorageAccountType(s string) interface{} {
|
|
||||||
for _, v := range compute.PossibleDiskStorageAccountTypesValues() {
|
|
||||||
if compute.DiskStorageAccountTypes(s) == v {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("%q is not a valid value %v",
|
|
||||||
s, compute.PossibleDiskStorageAccountTypesValues())
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkHyperVGeneration(s string) interface{} {
|
|
||||||
for _, v := range compute.PossibleHyperVGenerationValues() {
|
|
||||||
if compute.HyperVGeneration(s) == v {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("%q is not a valid value %v",
|
|
||||||
s, compute.PossibleHyperVGenerationValues())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "linux", "freebsd":
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return nil, errors.New("the azure-chroot builder only works on Linux and FreeBSD environments")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := b.config.ClientConfig.FillParameters()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error setting Azure client defaults: %v", err)
|
|
||||||
}
|
|
||||||
azcli, err := client.New(b.config.ClientConfig, ui.Say)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error creating Azure client: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
wrappedCommand := func(command string) (string, error) {
|
|
||||||
ictx := b.config.ctx
|
|
||||||
ictx.Data = &struct{ Command string }{Command: command}
|
|
||||||
return interpolate.Render(b.config.CommandWrapper, &ictx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the state bag and initial state for the steps
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("config", &b.config)
|
|
||||||
state.Put("hook", hook)
|
|
||||||
state.Put("ui", ui)
|
|
||||||
state.Put("azureclient", azcli)
|
|
||||||
state.Put("wrappedCommand", common.CommandWrapper(wrappedCommand))
|
|
||||||
|
|
||||||
info, err := azcli.MetadataClient().GetComputeInfo()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("MetadataClient().GetComputeInfo(): error: %+v", err)
|
|
||||||
err := fmt.Errorf(
|
|
||||||
"Error retrieving information ARM resource ID and location" +
|
|
||||||
"of the VM that Packer is running on.\n" +
|
|
||||||
"Please verify that Packer is running on a proper Azure VM.")
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
state.Put("instance", info)
|
|
||||||
|
|
||||||
// Build the step array from the config
|
|
||||||
steps := buildsteps(b.config, info)
|
|
||||||
|
|
||||||
// Run!
|
|
||||||
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
|
|
||||||
b.runner.Run(ctx, state)
|
|
||||||
|
|
||||||
// If there was an error, return that
|
|
||||||
if rawErr, ok := state.GetOk("error"); ok {
|
|
||||||
return nil, rawErr.(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the artifact and return it
|
|
||||||
artifact := &azcommon.Artifact{
|
|
||||||
BuilderIdValue: BuilderID,
|
|
||||||
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
|
|
||||||
AzureClientSet: azcli,
|
|
||||||
}
|
|
||||||
if b.config.ImageResourceID != "" {
|
|
||||||
artifact.Resources = append(artifact.Resources, b.config.ImageResourceID)
|
|
||||||
}
|
|
||||||
if e, _ := b.config.SharedImageGalleryDestination.Validate(""); len(e) == 0 {
|
|
||||||
artifact.Resources = append(artifact.Resources, b.config.SharedImageGalleryDestination.ResourceID(info.SubscriptionID))
|
|
||||||
}
|
|
||||||
if b.config.SkipCleanup {
|
|
||||||
if d, ok := state.GetOk(stateBagKey_Diskset); ok {
|
|
||||||
for _, disk := range d.(Diskset) {
|
|
||||||
artifact.Resources = append(artifact.Resources, disk.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if d, ok := state.GetOk(stateBagKey_Snapshotset); ok {
|
|
||||||
for _, snapshot := range d.(Diskset) {
|
|
||||||
artifact.Resources = append(artifact.Resources, snapshot.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return artifact, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildsteps(config Config, info *client.ComputeInfo) []multistep.Step {
|
|
||||||
// Build the steps
|
|
||||||
var steps []multistep.Step
|
|
||||||
addSteps := func(s ...multistep.Step) { // convenience function
|
|
||||||
steps = append(steps, s...)
|
|
||||||
}
|
|
||||||
|
|
||||||
e, _ := config.SharedImageGalleryDestination.Validate("")
|
|
||||||
hasValidSharedImage := len(e) == 0
|
|
||||||
|
|
||||||
if hasValidSharedImage {
|
|
||||||
// validate destination early
|
|
||||||
addSteps(
|
|
||||||
&StepVerifySharedImageDestination{
|
|
||||||
Image: config.SharedImageGalleryDestination,
|
|
||||||
Location: info.Location,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.FromScratch {
|
|
||||||
addSteps(&StepCreateNewDiskset{
|
|
||||||
OSDiskID: config.TemporaryOSDiskID,
|
|
||||||
OSDiskSizeGB: config.OSDiskSizeGB,
|
|
||||||
OSDiskStorageAccountType: config.OSDiskStorageAccountType,
|
|
||||||
HyperVGeneration: config.ImageHyperVGeneration,
|
|
||||||
Location: info.Location})
|
|
||||||
} else {
|
|
||||||
switch config.sourceType {
|
|
||||||
case sourcePlatformImage:
|
|
||||||
if pi, err := client.ParsePlatformImageURN(config.Source); err == nil {
|
|
||||||
if strings.EqualFold(pi.Version, "latest") {
|
|
||||||
addSteps(
|
|
||||||
&StepResolvePlatformImageVersion{
|
|
||||||
PlatformImage: pi,
|
|
||||||
Location: info.Location,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
addSteps(
|
|
||||||
&StepCreateNewDiskset{
|
|
||||||
OSDiskID: config.TemporaryOSDiskID,
|
|
||||||
OSDiskSizeGB: config.OSDiskSizeGB,
|
|
||||||
OSDiskStorageAccountType: config.OSDiskStorageAccountType,
|
|
||||||
HyperVGeneration: config.ImageHyperVGeneration,
|
|
||||||
Location: info.Location,
|
|
||||||
SourcePlatformImage: pi,
|
|
||||||
|
|
||||||
SkipCleanup: config.SkipCleanup,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
panic("Couldn't parse platfrom image urn: " + config.Source + " err: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
case sourceDisk:
|
|
||||||
addSteps(
|
|
||||||
&StepVerifySourceDisk{
|
|
||||||
SourceDiskResourceID: config.Source,
|
|
||||||
Location: info.Location,
|
|
||||||
},
|
|
||||||
&StepCreateNewDiskset{
|
|
||||||
OSDiskID: config.TemporaryOSDiskID,
|
|
||||||
OSDiskSizeGB: config.OSDiskSizeGB,
|
|
||||||
OSDiskStorageAccountType: config.OSDiskStorageAccountType,
|
|
||||||
HyperVGeneration: config.ImageHyperVGeneration,
|
|
||||||
SourceOSDiskResourceID: config.Source,
|
|
||||||
Location: info.Location,
|
|
||||||
|
|
||||||
SkipCleanup: config.SkipCleanup,
|
|
||||||
})
|
|
||||||
|
|
||||||
case sourceSharedImage:
|
|
||||||
addSteps(
|
|
||||||
&StepVerifySharedImageSource{
|
|
||||||
SharedImageID: config.Source,
|
|
||||||
SubscriptionID: info.SubscriptionID,
|
|
||||||
Location: info.Location,
|
|
||||||
},
|
|
||||||
&StepCreateNewDiskset{
|
|
||||||
OSDiskID: config.TemporaryOSDiskID,
|
|
||||||
DataDiskIDPrefix: config.TemporaryDataDiskIDPrefix,
|
|
||||||
OSDiskSizeGB: config.OSDiskSizeGB,
|
|
||||||
OSDiskStorageAccountType: config.OSDiskStorageAccountType,
|
|
||||||
DataDiskStorageAccountType: config.DataDiskStorageAccountType,
|
|
||||||
SourceImageResourceID: config.Source,
|
|
||||||
Location: info.Location,
|
|
||||||
|
|
||||||
SkipCleanup: config.SkipCleanup,
|
|
||||||
})
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("Unknown source type: %+q", config.sourceType))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addSteps(
|
|
||||||
&StepAttachDisk{}, // uses os_disk_resource_id and sets 'device' in stateBag
|
|
||||||
&chroot.StepPreMountCommands{
|
|
||||||
Commands: config.PreMountCommands,
|
|
||||||
},
|
|
||||||
&StepMountDevice{
|
|
||||||
MountOptions: config.MountOptions,
|
|
||||||
MountPartition: config.MountPartition,
|
|
||||||
MountPath: config.MountPath,
|
|
||||||
},
|
|
||||||
&chroot.StepPostMountCommands{
|
|
||||||
Commands: config.PostMountCommands,
|
|
||||||
},
|
|
||||||
&chroot.StepMountExtra{
|
|
||||||
ChrootMounts: config.ChrootMounts,
|
|
||||||
},
|
|
||||||
&chroot.StepCopyFiles{
|
|
||||||
Files: config.CopyFiles,
|
|
||||||
},
|
|
||||||
&chroot.StepChrootProvision{},
|
|
||||||
&chroot.StepEarlyCleanup{},
|
|
||||||
)
|
|
||||||
|
|
||||||
if config.ImageResourceID != "" {
|
|
||||||
addSteps(&StepCreateImage{
|
|
||||||
ImageResourceID: config.ImageResourceID,
|
|
||||||
ImageOSState: string(compute.Generalized),
|
|
||||||
OSDiskCacheType: config.OSDiskCacheType,
|
|
||||||
OSDiskStorageAccountType: config.OSDiskStorageAccountType,
|
|
||||||
Location: info.Location,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if hasValidSharedImage {
|
|
||||||
addSteps(
|
|
||||||
&StepCreateSnapshotset{
|
|
||||||
OSDiskSnapshotID: config.TemporaryOSDiskSnapshotID,
|
|
||||||
DataDiskSnapshotIDPrefix: config.TemporaryDataDiskSnapshotIDPrefix,
|
|
||||||
Location: info.Location,
|
|
||||||
SkipCleanup: config.SkipCleanup,
|
|
||||||
},
|
|
||||||
&StepCreateSharedImageVersion{
|
|
||||||
Destination: config.SharedImageGalleryDestination,
|
|
||||||
OSDiskCacheType: config.OSDiskCacheType,
|
|
||||||
Location: info.Location,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return steps
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FlatConfig is an auto-generated flat version of Config.
|
|
||||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
type FlatConfig struct {
|
|
||||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
|
||||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
|
||||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
|
||||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
|
||||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
|
||||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
|
||||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
|
||||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
|
||||||
CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name" hcl:"cloud_environment_name"`
|
|
||||||
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
|
|
||||||
ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"`
|
|
||||||
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"`
|
|
||||||
ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"`
|
|
||||||
ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"`
|
|
||||||
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
|
|
||||||
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
|
|
||||||
SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"`
|
|
||||||
UseAzureCLIAuth *bool `mapstructure:"use_azure_cli_auth" required:"false" cty:"use_azure_cli_auth" hcl:"use_azure_cli_auth"`
|
|
||||||
FromScratch *bool `mapstructure:"from_scratch" cty:"from_scratch" hcl:"from_scratch"`
|
|
||||||
Source *string `mapstructure:"source" required:"true" cty:"source" hcl:"source"`
|
|
||||||
CommandWrapper *string `mapstructure:"command_wrapper" cty:"command_wrapper" hcl:"command_wrapper"`
|
|
||||||
PreMountCommands []string `mapstructure:"pre_mount_commands" cty:"pre_mount_commands" hcl:"pre_mount_commands"`
|
|
||||||
MountOptions []string `mapstructure:"mount_options" cty:"mount_options" hcl:"mount_options"`
|
|
||||||
MountPartition *string `mapstructure:"mount_partition" cty:"mount_partition" hcl:"mount_partition"`
|
|
||||||
MountPath *string `mapstructure:"mount_path" cty:"mount_path" hcl:"mount_path"`
|
|
||||||
PostMountCommands []string `mapstructure:"post_mount_commands" cty:"post_mount_commands" hcl:"post_mount_commands"`
|
|
||||||
ChrootMounts [][]string `mapstructure:"chroot_mounts" cty:"chroot_mounts" hcl:"chroot_mounts"`
|
|
||||||
CopyFiles []string `mapstructure:"copy_files" cty:"copy_files" hcl:"copy_files"`
|
|
||||||
OSDiskSizeGB *int32 `mapstructure:"os_disk_size_gb" cty:"os_disk_size_gb" hcl:"os_disk_size_gb"`
|
|
||||||
OSDiskStorageAccountType *string `mapstructure:"os_disk_storage_account_type" cty:"os_disk_storage_account_type" hcl:"os_disk_storage_account_type"`
|
|
||||||
OSDiskCacheType *string `mapstructure:"os_disk_cache_type" cty:"os_disk_cache_type" hcl:"os_disk_cache_type"`
|
|
||||||
DataDiskStorageAccountType *string `mapstructure:"data_disk_storage_account_type" cty:"data_disk_storage_account_type" hcl:"data_disk_storage_account_type"`
|
|
||||||
DataDiskCacheType *string `mapstructure:"data_disk_cache_type" cty:"data_disk_cache_type" hcl:"data_disk_cache_type"`
|
|
||||||
ImageHyperVGeneration *string `mapstructure:"image_hyperv_generation" cty:"image_hyperv_generation" hcl:"image_hyperv_generation"`
|
|
||||||
TemporaryOSDiskID *string `mapstructure:"temporary_os_disk_id" cty:"temporary_os_disk_id" hcl:"temporary_os_disk_id"`
|
|
||||||
TemporaryOSDiskSnapshotID *string `mapstructure:"temporary_os_disk_snapshot_id" cty:"temporary_os_disk_snapshot_id" hcl:"temporary_os_disk_snapshot_id"`
|
|
||||||
TemporaryDataDiskIDPrefix *string `mapstructure:"temporary_data_disk_id_prefix" cty:"temporary_data_disk_id_prefix" hcl:"temporary_data_disk_id_prefix"`
|
|
||||||
TemporaryDataDiskSnapshotIDPrefix *string `mapstructure:"temporary_data_disk_snapshot_id" cty:"temporary_data_disk_snapshot_id" hcl:"temporary_data_disk_snapshot_id"`
|
|
||||||
SkipCleanup *bool `mapstructure:"skip_cleanup" cty:"skip_cleanup" hcl:"skip_cleanup"`
|
|
||||||
ImageResourceID *string `mapstructure:"image_resource_id" cty:"image_resource_id" hcl:"image_resource_id"`
|
|
||||||
SharedImageGalleryDestination *FlatSharedImageGalleryDestination `mapstructure:"shared_image_destination" cty:"shared_image_destination" hcl:"shared_image_destination"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatConfig.
|
|
||||||
// FlatConfig is an auto-generated flat version of Config.
|
|
||||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
|
||||||
return new(FlatConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HCL2Spec returns the hcl spec of a Config.
|
|
||||||
// This spec is used by HCL to read the fields of Config.
|
|
||||||
// The decoded values from this spec will then be applied to a FlatConfig.
|
|
||||||
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|
||||||
s := map[string]hcldec.Spec{
|
|
||||||
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
|
|
||||||
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
|
|
||||||
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
|
|
||||||
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
|
|
||||||
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
|
|
||||||
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
|
|
||||||
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
|
|
||||||
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
|
||||||
"cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false},
|
|
||||||
"client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false},
|
|
||||||
"client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false},
|
|
||||||
"client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false},
|
|
||||||
"client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false},
|
|
||||||
"client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false},
|
|
||||||
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
|
||||||
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
|
||||||
"subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false},
|
|
||||||
"use_azure_cli_auth": &hcldec.AttrSpec{Name: "use_azure_cli_auth", Type: cty.Bool, Required: false},
|
|
||||||
"from_scratch": &hcldec.AttrSpec{Name: "from_scratch", Type: cty.Bool, Required: false},
|
|
||||||
"source": &hcldec.AttrSpec{Name: "source", Type: cty.String, Required: false},
|
|
||||||
"command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false},
|
|
||||||
"pre_mount_commands": &hcldec.AttrSpec{Name: "pre_mount_commands", Type: cty.List(cty.String), Required: false},
|
|
||||||
"mount_options": &hcldec.AttrSpec{Name: "mount_options", Type: cty.List(cty.String), Required: false},
|
|
||||||
"mount_partition": &hcldec.AttrSpec{Name: "mount_partition", Type: cty.String, Required: false},
|
|
||||||
"mount_path": &hcldec.AttrSpec{Name: "mount_path", Type: cty.String, Required: false},
|
|
||||||
"post_mount_commands": &hcldec.AttrSpec{Name: "post_mount_commands", Type: cty.List(cty.String), Required: false},
|
|
||||||
"chroot_mounts": &hcldec.AttrSpec{Name: "chroot_mounts", Type: cty.List(cty.List(cty.String)), Required: false},
|
|
||||||
"copy_files": &hcldec.AttrSpec{Name: "copy_files", Type: cty.List(cty.String), Required: false},
|
|
||||||
"os_disk_size_gb": &hcldec.AttrSpec{Name: "os_disk_size_gb", Type: cty.Number, Required: false},
|
|
||||||
"os_disk_storage_account_type": &hcldec.AttrSpec{Name: "os_disk_storage_account_type", Type: cty.String, Required: false},
|
|
||||||
"os_disk_cache_type": &hcldec.AttrSpec{Name: "os_disk_cache_type", Type: cty.String, Required: false},
|
|
||||||
"data_disk_storage_account_type": &hcldec.AttrSpec{Name: "data_disk_storage_account_type", Type: cty.String, Required: false},
|
|
||||||
"data_disk_cache_type": &hcldec.AttrSpec{Name: "data_disk_cache_type", Type: cty.String, Required: false},
|
|
||||||
"image_hyperv_generation": &hcldec.AttrSpec{Name: "image_hyperv_generation", Type: cty.String, Required: false},
|
|
||||||
"temporary_os_disk_id": &hcldec.AttrSpec{Name: "temporary_os_disk_id", Type: cty.String, Required: false},
|
|
||||||
"temporary_os_disk_snapshot_id": &hcldec.AttrSpec{Name: "temporary_os_disk_snapshot_id", Type: cty.String, Required: false},
|
|
||||||
"temporary_data_disk_id_prefix": &hcldec.AttrSpec{Name: "temporary_data_disk_id_prefix", Type: cty.String, Required: false},
|
|
||||||
"temporary_data_disk_snapshot_id": &hcldec.AttrSpec{Name: "temporary_data_disk_snapshot_id", Type: cty.String, Required: false},
|
|
||||||
"skip_cleanup": &hcldec.AttrSpec{Name: "skip_cleanup", Type: cty.Bool, Required: false},
|
|
||||||
"image_resource_id": &hcldec.AttrSpec{Name: "image_resource_id", Type: cty.String, Required: false},
|
|
||||||
"shared_image_destination": &hcldec.BlockSpec{TypeName: "shared_image_destination", Nested: hcldec.ObjectSpec((*FlatSharedImageGalleryDestination)(nil).HCL2Spec())},
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
|
@ -1,234 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBuilder_Prepare(t *testing.T) {
|
|
||||||
type config map[string]interface{}
|
|
||||||
type regexMatchers map[string]string // map of regex : error message
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
config config
|
|
||||||
validate func(Config)
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "platform image to managed disk",
|
|
||||||
config: config{
|
|
||||||
"client_id": "123",
|
|
||||||
"client_secret": "456",
|
|
||||||
"subscription_id": "789",
|
|
||||||
"source": "credativ:Debian:9:latest",
|
|
||||||
"image_resource_id": "/subscriptions/789/resourceGroups/otherrgname/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}",
|
|
||||||
"shared_image_destination": config{
|
|
||||||
"resource_group": "otherrgname",
|
|
||||||
"gallery_name": "myGallery",
|
|
||||||
"image_name": "imageName",
|
|
||||||
"image_version": "1.0.2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
validate: func(c Config) {
|
|
||||||
if c.OSDiskSizeGB != 0 {
|
|
||||||
t.Errorf("Expected OSDiskSizeGB to be 0, was %+v", c.OSDiskSizeGB)
|
|
||||||
}
|
|
||||||
if c.MountPartition != "1" {
|
|
||||||
t.Errorf("Expected MountPartition to be %s, but found %s", "1", c.MountPartition)
|
|
||||||
}
|
|
||||||
if c.OSDiskStorageAccountType != string(compute.PremiumLRS) {
|
|
||||||
t.Errorf("Expected OSDiskStorageAccountType to be %s, but found %s", string(compute.PremiumLRS), c.OSDiskStorageAccountType)
|
|
||||||
}
|
|
||||||
if c.OSDiskCacheType != string(compute.CachingTypesReadOnly) {
|
|
||||||
t.Errorf("Expected OSDiskCacheType to be %s, but found %s", string(compute.CachingTypesReadOnly), c.OSDiskCacheType)
|
|
||||||
}
|
|
||||||
if c.ImageHyperVGeneration != string(compute.V1) {
|
|
||||||
t.Errorf("Expected ImageHyperVGeneration to be %s, but found %s", string(compute.V1), c.ImageHyperVGeneration)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "disk to managed image, validate temp disk id expansion",
|
|
||||||
config: config{
|
|
||||||
"source": "/subscriptions/789/resourceGroups/testrg/providers/Microsoft.Compute/disks/diskname",
|
|
||||||
"image_resource_id": "/subscriptions/789/resourceGroups/otherrgname/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}",
|
|
||||||
},
|
|
||||||
validate: func(c Config) {
|
|
||||||
prefix := "/subscriptions/testSubscriptionID/resourceGroups/testResourceGroup/providers/Microsoft.Compute/disks/PackerTemp-osdisk-"
|
|
||||||
if !strings.HasPrefix(c.TemporaryOSDiskID, prefix) {
|
|
||||||
t.Errorf("Expected TemporaryOSDiskID to start with %q, but got %q", prefix, c.TemporaryOSDiskID)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "disk to both managed image and shared image",
|
|
||||||
config: config{
|
|
||||||
"source": "/subscriptions/789/resourceGroups/testrg/providers/Microsoft.Compute/disks/diskname",
|
|
||||||
"image_resource_id": "/subscriptions/789/resourceGroups/otherrgname/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}",
|
|
||||||
"shared_image_destination": config{
|
|
||||||
"resource_group": "rg",
|
|
||||||
"gallery_name": "galleryName",
|
|
||||||
"image_name": "imageName",
|
|
||||||
"image_version": "0.1.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "disk to both managed image and shared image with missing property",
|
|
||||||
config: config{
|
|
||||||
"source": "/subscriptions/789/resourceGroups/testrg/providers/Microsoft.Compute/disks/diskname",
|
|
||||||
"image_resource_id": "/subscriptions/789/resourceGroups/otherrgname/providers/Microsoft.Compute/images/MyDebianOSImage-{{timestamp}}",
|
|
||||||
"shared_image_destination": config{
|
|
||||||
"resource_group": "rg",
|
|
||||||
"gallery_name": "galleryName",
|
|
||||||
"image_version": "0.1.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "from shared image",
|
|
||||||
config: config{
|
|
||||||
"shared_image_destination": config{
|
|
||||||
"resource_group": "otherrgname",
|
|
||||||
"gallery_name": "myGallery",
|
|
||||||
"image_name": "imageName",
|
|
||||||
"image_version": "1.0.2",
|
|
||||||
},
|
|
||||||
"source": "/subscriptions/789/resourceGroups/testrg/providers/Microsoft.Compute/disks/diskname",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "err: no output",
|
|
||||||
config: config{
|
|
||||||
"source": "/subscriptions/789/resourceGroups/testrg/providers/Microsoft.Compute/disks/diskname",
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
withMetadataStub(func() {
|
|
||||||
b := &Builder{}
|
|
||||||
|
|
||||||
_, _, err := b.Prepare(tt.config)
|
|
||||||
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("Builder.Prepare() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if tt.validate != nil {
|
|
||||||
tt.validate(b.config)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_buildsteps(t *testing.T) {
|
|
||||||
info := &client.ComputeInfo{
|
|
||||||
Location: "northpole",
|
|
||||||
Name: "unittestVM",
|
|
||||||
ResourceGroupName: "unittestResourceGroup",
|
|
||||||
SubscriptionID: "96854241-60c7-426d-9a27-3fdeec8957f4",
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
config Config
|
|
||||||
verify func([]multistep.Step, *testing.T)
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Source FromScrath creates empty disk",
|
|
||||||
config: Config{FromScratch: true},
|
|
||||||
verify: func(steps []multistep.Step, _ *testing.T) {
|
|
||||||
for _, s := range steps {
|
|
||||||
if s, ok := s.(*StepCreateNewDiskset); ok {
|
|
||||||
if s.SourceOSDiskResourceID == "" &&
|
|
||||||
s.SourcePlatformImage == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Errorf("found misconfigured StepCreateNewDisk: %+v", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Error("did not find a StepCreateNewDisk")
|
|
||||||
}},
|
|
||||||
{
|
|
||||||
name: "Source Platform image disk creation",
|
|
||||||
config: Config{Source: "publisher:offer:sku:version", sourceType: sourcePlatformImage},
|
|
||||||
verify: func(steps []multistep.Step, _ *testing.T) {
|
|
||||||
for _, s := range steps {
|
|
||||||
if s, ok := s.(*StepCreateNewDiskset); ok {
|
|
||||||
if s.SourceOSDiskResourceID == "" &&
|
|
||||||
s.SourcePlatformImage != nil &&
|
|
||||||
s.SourcePlatformImage.Publisher == "publisher" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Errorf("found misconfigured StepCreateNewDisk: %+v", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Error("did not find a StepCreateNewDisk")
|
|
||||||
}},
|
|
||||||
{
|
|
||||||
name: "Source Platform image with version latest adds StepResolvePlatformImageVersion",
|
|
||||||
config: Config{Source: "publisher:offer:sku:latest", sourceType: sourcePlatformImage},
|
|
||||||
verify: func(steps []multistep.Step, _ *testing.T) {
|
|
||||||
for _, s := range steps {
|
|
||||||
if s, ok := s.(*StepResolvePlatformImageVersion); ok {
|
|
||||||
if s.PlatformImage != nil &&
|
|
||||||
s.Location == info.Location {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Errorf("found misconfigured StepResolvePlatformImageVersion: %+v", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Error("did not find a StepResolvePlatformImageVersion")
|
|
||||||
}},
|
|
||||||
{
|
|
||||||
name: "Source Disk adds correct disk creation",
|
|
||||||
config: Config{Source: "diskresourceid", sourceType: sourceDisk},
|
|
||||||
verify: func(steps []multistep.Step, _ *testing.T) {
|
|
||||||
for _, s := range steps {
|
|
||||||
if s, ok := s.(*StepCreateNewDiskset); ok {
|
|
||||||
if s.SourceOSDiskResourceID == "diskresourceid" &&
|
|
||||||
s.SourcePlatformImage == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Errorf("found misconfigured StepCreateNewDisk: %+v", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Error("did not find a StepCreateNewDisk")
|
|
||||||
}},
|
|
||||||
{
|
|
||||||
name: "Source disk adds StepVerifySourceDisk",
|
|
||||||
config: Config{Source: "diskresourceid", sourceType: sourceDisk},
|
|
||||||
verify: func(steps []multistep.Step, _ *testing.T) {
|
|
||||||
for _, s := range steps {
|
|
||||||
if s, ok := s.(*StepVerifySourceDisk); ok {
|
|
||||||
if s.SourceDiskResourceID == "diskresourceid" &&
|
|
||||||
s.Location == info.Location {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Errorf("found misconfigured StepVerifySourceDisk: %+v", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Error("did not find a StepVerifySourceDisk")
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
withMetadataStub(func() { // ensure that values are taken from info, instead of retrieved again
|
|
||||||
got := buildsteps(tt.config, info)
|
|
||||||
tt.verify(got, t)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
const (
|
|
||||||
stateBagKey_Diskset = "diskset"
|
|
||||||
stateBagKey_Snapshotset = "snapshotset"
|
|
||||||
)
|
|
|
@ -1,190 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
|
||||||
"github.com/Azure/go-autorest/autorest/to"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DiskAttacher interface {
|
|
||||||
AttachDisk(ctx context.Context, disk string) (lun int32, err error)
|
|
||||||
WaitForDevice(ctx context.Context, i int32) (device string, err error)
|
|
||||||
DetachDisk(ctx context.Context, disk string) (err error)
|
|
||||||
WaitForDetach(ctx context.Context, diskID string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
var NewDiskAttacher = func(azureClient client.AzureClientSet) DiskAttacher {
|
|
||||||
return &diskAttacher{
|
|
||||||
azcli: azureClient,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type diskAttacher struct {
|
|
||||||
azcli client.AzureClientSet
|
|
||||||
|
|
||||||
vm *client.ComputeInfo // store info about this VM so that we don't have to ask metadata service on every call
|
|
||||||
}
|
|
||||||
|
|
||||||
var DiskNotFoundError = errors.New("Disk not found")
|
|
||||||
|
|
||||||
func (da *diskAttacher) DetachDisk(ctx context.Context, diskID string) error {
|
|
||||||
log.Println("Fetching list of disks currently attached to VM")
|
|
||||||
currentDisks, err := da.getDisks(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Removing %q from list of disks currently attached to VM", diskID)
|
|
||||||
newDisks := []compute.DataDisk{}
|
|
||||||
for _, disk := range currentDisks {
|
|
||||||
if disk.ManagedDisk != nil &&
|
|
||||||
!strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) {
|
|
||||||
newDisks = append(newDisks, disk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(currentDisks) == len(newDisks) {
|
|
||||||
return DiskNotFoundError
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Updating new list of disks attached to VM")
|
|
||||||
err = da.setDisks(ctx, newDisks)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (da *diskAttacher) WaitForDetach(ctx context.Context, diskID string) error {
|
|
||||||
for { // loop until disk is not attached, timeout or error
|
|
||||||
list, err := da.getDisks(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if findDiskInList(list, diskID) == nil {
|
|
||||||
log.Println("Disk is no longer in VM model, assuming detached")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(time.Second): //continue
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (da *diskAttacher) AttachDisk(ctx context.Context, diskID string) (int32, error) {
|
|
||||||
dataDisks, err := da.getDisks(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check to see if disk is already attached, remember lun if found
|
|
||||||
if disk := findDiskInList(dataDisks, diskID); disk != nil {
|
|
||||||
// disk is already attached, just take this lun
|
|
||||||
if disk.Lun == nil {
|
|
||||||
return -1, errors.New("disk is attached, but lun was not set in VM model (possibly an error in the Azure APIs)")
|
|
||||||
}
|
|
||||||
return to.Int32(disk.Lun), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// disk was not found on VM, go and actually attach it
|
|
||||||
|
|
||||||
var lun int32 = -1
|
|
||||||
findFreeLun:
|
|
||||||
for lun = 0; lun < 64; lun++ {
|
|
||||||
for _, v := range dataDisks {
|
|
||||||
if to.Int32(v.Lun) == lun {
|
|
||||||
continue findFreeLun
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no datadisk is using this lun
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// append new data disk to collection
|
|
||||||
dataDisks = append(dataDisks, compute.DataDisk{
|
|
||||||
CreateOption: compute.DiskCreateOptionTypesAttach,
|
|
||||||
ManagedDisk: &compute.ManagedDiskParameters{
|
|
||||||
ID: to.StringPtr(diskID),
|
|
||||||
},
|
|
||||||
Lun: to.Int32Ptr(lun),
|
|
||||||
})
|
|
||||||
|
|
||||||
// prepare resource object for update operation
|
|
||||||
err = da.setDisks(ctx, dataDisks)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return lun, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (da *diskAttacher) getThisVM(ctx context.Context) (compute.VirtualMachine, error) {
|
|
||||||
// getting resource info for this VM
|
|
||||||
if da.vm == nil {
|
|
||||||
vm, err := da.azcli.MetadataClient().GetComputeInfo()
|
|
||||||
if err != nil {
|
|
||||||
return compute.VirtualMachine{}, err
|
|
||||||
}
|
|
||||||
da.vm = vm
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve actual VM
|
|
||||||
vmResource, err := da.azcli.VirtualMachinesClient().Get(ctx, da.vm.ResourceGroupName, da.vm.Name, "")
|
|
||||||
if err != nil {
|
|
||||||
return compute.VirtualMachine{}, err
|
|
||||||
}
|
|
||||||
if vmResource.StorageProfile == nil {
|
|
||||||
return compute.VirtualMachine{}, errors.New("properties.storageProfile is not set on VM, this is unexpected")
|
|
||||||
}
|
|
||||||
|
|
||||||
return vmResource, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (da diskAttacher) getDisks(ctx context.Context) ([]compute.DataDisk, error) {
|
|
||||||
vmResource, err := da.getThisVM(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return []compute.DataDisk{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return *vmResource.StorageProfile.DataDisks, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (da diskAttacher) setDisks(ctx context.Context, disks []compute.DataDisk) error {
|
|
||||||
vmResource, err := da.getThisVM(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := azure.ParseResourceID(to.String(vmResource.ID))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
vmResource.StorageProfile.DataDisks = &disks
|
|
||||||
vmResource.Resources = nil
|
|
||||||
|
|
||||||
// update the VM resource, attach disk
|
|
||||||
_, err = da.azcli.VirtualMachinesClient().CreateOrUpdate(ctx, id.ResourceGroup, id.ResourceName, vmResource)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func findDiskInList(list []compute.DataDisk, diskID string) *compute.DataDisk {
|
|
||||||
for _, disk := range list {
|
|
||||||
if disk.ManagedDisk != nil &&
|
|
||||||
strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) {
|
|
||||||
return &disk
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) {
|
|
||||||
// This builder will always be running in Azure, where data disks show up
|
|
||||||
// on scbus5 target 0. The camcontrol command always outputs LUNs in
|
|
||||||
// unpadded hexadecimal format.
|
|
||||||
regexStr := fmt.Sprintf(`at scbus5 target 0 lun %x \(.*?da([\d]+)`, lun)
|
|
||||||
devRegex, err := regexp.Compile(regexStr)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
cmd := exec.Command("camcontrol", "devlist")
|
|
||||||
var out bytes.Buffer
|
|
||||||
cmd.Stdout = &out
|
|
||||||
err = cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
outString := out.String()
|
|
||||||
scanner := bufio.NewScanner(strings.NewReader(outString))
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
// Check if this is the correct bus, target, and LUN.
|
|
||||||
if matches := devRegex.FindStringSubmatch(line); matches != nil {
|
|
||||||
// If this function immediately returns, devfs won't have
|
|
||||||
// created the device yet.
|
|
||||||
time.Sleep(1000 * time.Millisecond)
|
|
||||||
return fmt.Sprintf("/dev/da%s", matches[1]), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = scanner.Err(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(100 * time.Millisecond):
|
|
||||||
// continue
|
|
||||||
case <-ctx.Done():
|
|
||||||
return "", ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func diskPathForLun(lun int32) string {
|
|
||||||
return fmt.Sprintf("/dev/disk/azure/scsi1/lun%d", lun)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) {
|
|
||||||
path := diskPathForLun(lun)
|
|
||||||
|
|
||||||
for {
|
|
||||||
link, err := os.Readlink(path)
|
|
||||||
if err == nil {
|
|
||||||
return filepath.Abs("/dev/disk/azure/scsi1/" + link)
|
|
||||||
} else if err != os.ErrNotExist {
|
|
||||||
if pe, ok := err.(*os.PathError); ok && pe.Err != syscall.ENOENT {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(100 * time.Millisecond):
|
|
||||||
// continue
|
|
||||||
case <-ctx.Done():
|
|
||||||
return "", ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// +build !linux,!freebsd
|
|
||||||
|
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) {
|
|
||||||
panic("The azure-chroot builder does not work on this platform.")
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Azure/go-autorest/autorest/to"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
testvm = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testGroup/Microsoft.Compute/virtualMachines/testVM"
|
|
||||||
testdisk = "/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/testGroup2/Microsoft.Compute/disks/testDisk"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Tests assume current machine is capable of running chroot builder (i.e. an Azure VM)
|
|
||||||
|
|
||||||
func Test_DiskAttacherAttachesDiskToVM(t *testing.T) {
|
|
||||||
azcli, err := client.GetTestClientSet(t) // integration test
|
|
||||||
require.Nil(t, err)
|
|
||||||
da := NewDiskAttacher(azcli)
|
|
||||||
testDiskName := t.Name()
|
|
||||||
|
|
||||||
vm, err := azcli.MetadataClient().GetComputeInfo()
|
|
||||||
require.Nil(t, err, "Test needs to run on an Azure VM, unable to retrieve VM information")
|
|
||||||
t.Log("Creating new disk '", testDiskName, "' in ", vm.ResourceGroupName)
|
|
||||||
|
|
||||||
disk, err := azcli.DisksClient().Get(context.TODO(), vm.ResourceGroupName, testDiskName)
|
|
||||||
if err == nil {
|
|
||||||
t.Log("Disk already exists")
|
|
||||||
if disk.DiskState == compute.Attached {
|
|
||||||
t.Log("Disk is attached, assuming to this machine, trying to detach")
|
|
||||||
err = da.DetachDisk(context.TODO(), to.String(disk.ID))
|
|
||||||
require.Nil(t, err)
|
|
||||||
}
|
|
||||||
t.Log("Deleting disk")
|
|
||||||
result, err := azcli.DisksClient().Delete(context.TODO(), vm.ResourceGroupName, testDiskName)
|
|
||||||
require.Nil(t, err)
|
|
||||||
err = result.WaitForCompletionRef(context.TODO(), azcli.PollClient())
|
|
||||||
require.Nil(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Log("Creating disk")
|
|
||||||
r, err := azcli.DisksClient().CreateOrUpdate(context.TODO(), vm.ResourceGroupName, testDiskName, compute.Disk{
|
|
||||||
Location: to.StringPtr(vm.Location),
|
|
||||||
Sku: &compute.DiskSku{
|
|
||||||
Name: compute.StandardLRS,
|
|
||||||
},
|
|
||||||
DiskProperties: &compute.DiskProperties{
|
|
||||||
DiskSizeGB: to.Int32Ptr(30),
|
|
||||||
CreationData: &compute.CreationData{CreateOption: compute.Empty},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
require.Nil(t, err)
|
|
||||||
err = r.WaitForCompletionRef(context.TODO(), azcli.PollClient())
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
t.Log("Retrieving disk properties")
|
|
||||||
d, err := azcli.DisksClient().Get(context.TODO(), vm.ResourceGroupName, testDiskName)
|
|
||||||
require.Nil(t, err)
|
|
||||||
assert.NotNil(t, d)
|
|
||||||
|
|
||||||
t.Log("Attaching disk")
|
|
||||||
lun, err := da.AttachDisk(context.TODO(), to.String(d.ID))
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
t.Log("Waiting for device")
|
|
||||||
dev, err := da.WaitForDevice(context.TODO(), lun)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
t.Log("Device path:", dev)
|
|
||||||
|
|
||||||
t.Log("Detaching disk")
|
|
||||||
err = da.DetachDisk(context.TODO(), to.String(d.ID))
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
t.Log("Deleting disk")
|
|
||||||
result, err := azcli.DisksClient().Delete(context.TODO(), vm.ResourceGroupName, testDiskName)
|
|
||||||
if err == nil {
|
|
||||||
err = result.WaitForCompletionRef(context.TODO(), azcli.PollClient())
|
|
||||||
}
|
|
||||||
require.Nil(t, err)
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import "github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
|
|
||||||
// Diskset represents all of the disks or snapshots associated with an image.
|
|
||||||
// It maps lun to resource ids. The OS disk is stored with lun=-1.
|
|
||||||
type Diskset map[int32]client.Resource
|
|
||||||
|
|
||||||
// OS return the OS disk resource ID or nil if it is not assigned
|
|
||||||
func (ds Diskset) OS() *client.Resource {
|
|
||||||
if r, ok := ds[-1]; ok {
|
|
||||||
return &r
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data return the data disk resource ID or nil if it is not assigned
|
|
||||||
func (ds Diskset) Data(lun int32) *client.Resource {
|
|
||||||
if r, ok := ds[lun]; ok {
|
|
||||||
return &r
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import "github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
|
|
||||||
// diskset easily creates a diskset for testing
|
|
||||||
func diskset(ids ...string) Diskset {
|
|
||||||
diskset := make(Diskset)
|
|
||||||
for i, id := range ids {
|
|
||||||
r, err := client.ParseResourceID(id)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
diskset[int32(i-1)] = r
|
|
||||||
}
|
|
||||||
return diskset
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import "github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
|
|
||||||
func withMetadataStub(f func()) {
|
|
||||||
mdc := client.DefaultMetadataClient
|
|
||||||
defer func() { client.DefaultMetadataClient = mdc }()
|
|
||||||
client.DefaultMetadataClient = client.MetadataClientStub{
|
|
||||||
ComputeInfo: client.ComputeInfo{
|
|
||||||
SubscriptionID: "testSubscriptionID",
|
|
||||||
ResourceGroupName: "testResourceGroup",
|
|
||||||
Name: "testVM",
|
|
||||||
Location: "testLocation",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
f()
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// testUI returns a test ui plus a function to retrieve the errors written to the ui
|
|
||||||
func testUI() (packersdk.Ui, func() string) {
|
|
||||||
errorBuffer := &strings.Builder{}
|
|
||||||
ui := &packersdk.BasicUi{
|
|
||||||
Reader: strings.NewReader(""),
|
|
||||||
Writer: ioutil.Discard,
|
|
||||||
ErrorWriter: errorBuffer,
|
|
||||||
}
|
|
||||||
return ui, errorBuffer.String
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
//go:generate packer-sdc struct-markdown
|
|
||||||
//go:generate packer-sdc mapstructure-to-hcl2 -type SharedImageGalleryDestination,TargetRegion
|
|
||||||
|
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SharedImageGalleryDestination models an image version in a Shared
|
|
||||||
// Image Gallery that can be used as a destination.
|
|
||||||
type SharedImageGalleryDestination struct {
|
|
||||||
ResourceGroup string `mapstructure:"resource_group" required:"true"`
|
|
||||||
GalleryName string `mapstructure:"gallery_name" required:"true"`
|
|
||||||
ImageName string `mapstructure:"image_name" required:"true"`
|
|
||||||
ImageVersion string `mapstructure:"image_version" required:"true"`
|
|
||||||
|
|
||||||
TargetRegions []TargetRegion `mapstructure:"target_regions"`
|
|
||||||
ExcludeFromLatest bool `mapstructure:"exclude_from_latest"`
|
|
||||||
ExcludeFromLatestTypo bool `mapstructure:"exlude_from_latest" undocumented:"true"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TargetRegion describes a region where the shared image should be replicated
|
|
||||||
type TargetRegion struct {
|
|
||||||
// Name of the Azure region
|
|
||||||
Name string `mapstructure:"name" required:"true"`
|
|
||||||
// Number of replicas in this region. Default: 1
|
|
||||||
ReplicaCount int32 `mapstructure:"replicas"`
|
|
||||||
// Storage account type: Standard_LRS or Standard_ZRS. Default: Standard_ZRS
|
|
||||||
StorageAccountType string `mapstructure:"storage_account_type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResourceID returns the resource ID string
|
|
||||||
func (sigd SharedImageGalleryDestination) ResourceID(subscriptionID string) string {
|
|
||||||
return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/galleries/%s/images/%s/versions/%s",
|
|
||||||
subscriptionID,
|
|
||||||
sigd.ResourceGroup,
|
|
||||||
sigd.GalleryName,
|
|
||||||
sigd.ImageName,
|
|
||||||
sigd.ImageVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates that the values in the shared image are valid (without checking them on the network)
|
|
||||||
func (sigd *SharedImageGalleryDestination) Validate(prefix string) (errs []error, warns []string) {
|
|
||||||
if sigd.ResourceGroup == "" {
|
|
||||||
errs = append(errs, fmt.Errorf("%s.resource_group is required", prefix))
|
|
||||||
}
|
|
||||||
if sigd.GalleryName == "" {
|
|
||||||
errs = append(errs, fmt.Errorf("%s.gallery_name is required", prefix))
|
|
||||||
}
|
|
||||||
if sigd.ImageName == "" {
|
|
||||||
errs = append(errs, fmt.Errorf("%s.image_name is required", prefix))
|
|
||||||
}
|
|
||||||
if match, err := regexp.MatchString("^[0-9]+\\.[0-9]+\\.[0-9]+$", sigd.ImageVersion); !match {
|
|
||||||
if err != nil {
|
|
||||||
warns = append(warns, fmt.Sprintf("Error matching pattern for %s.image_version: %s (this is probably a bug)", prefix, err))
|
|
||||||
}
|
|
||||||
errs = append(errs, fmt.Errorf("%s.image_version should match '^[0-9]+\\.[0-9]+\\.[0-9]+$'", prefix))
|
|
||||||
}
|
|
||||||
if len(sigd.TargetRegions) == 0 {
|
|
||||||
warns = append(warns,
|
|
||||||
fmt.Sprintf("%s.target_regions is empty; image will only be available in the region of the gallery", prefix))
|
|
||||||
}
|
|
||||||
if sigd.ExcludeFromLatestTypo == true && sigd.ExcludeFromLatest == false {
|
|
||||||
warns = append(warns,
|
|
||||||
fmt.Sprintf("%s.exlude_from_latest is being deprecated, please use exclude_from_latest", prefix))
|
|
||||||
sigd.ExcludeFromLatest = sigd.ExcludeFromLatestTypo
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination.
|
|
||||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
type FlatSharedImageGalleryDestination struct {
|
|
||||||
ResourceGroup *string `mapstructure:"resource_group" required:"true" cty:"resource_group" hcl:"resource_group"`
|
|
||||||
GalleryName *string `mapstructure:"gallery_name" required:"true" cty:"gallery_name" hcl:"gallery_name"`
|
|
||||||
ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name" hcl:"image_name"`
|
|
||||||
ImageVersion *string `mapstructure:"image_version" required:"true" cty:"image_version" hcl:"image_version"`
|
|
||||||
TargetRegions []FlatTargetRegion `mapstructure:"target_regions" cty:"target_regions" hcl:"target_regions"`
|
|
||||||
ExcludeFromLatest *bool `mapstructure:"exclude_from_latest" cty:"exclude_from_latest" hcl:"exclude_from_latest"`
|
|
||||||
ExcludeFromLatestTypo *bool `mapstructure:"exlude_from_latest" undocumented:"true" cty:"exlude_from_latest" hcl:"exlude_from_latest"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatSharedImageGalleryDestination.
|
|
||||||
// FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination.
|
|
||||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
|
||||||
return new(FlatSharedImageGalleryDestination)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HCL2Spec returns the hcl spec of a SharedImageGalleryDestination.
|
|
||||||
// This spec is used by HCL to read the fields of SharedImageGalleryDestination.
|
|
||||||
// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination.
|
|
||||||
func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec {
|
|
||||||
s := map[string]hcldec.Spec{
|
|
||||||
"resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false},
|
|
||||||
"gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false},
|
|
||||||
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
|
||||||
"image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false},
|
|
||||||
"target_regions": &hcldec.BlockListSpec{TypeName: "target_regions", Nested: hcldec.ObjectSpec((*FlatTargetRegion)(nil).HCL2Spec())},
|
|
||||||
"exclude_from_latest": &hcldec.AttrSpec{Name: "exclude_from_latest", Type: cty.Bool, Required: false},
|
|
||||||
"exlude_from_latest": &hcldec.AttrSpec{Name: "exlude_from_latest", Type: cty.Bool, Required: false},
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatTargetRegion is an auto-generated flat version of TargetRegion.
|
|
||||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
type FlatTargetRegion struct {
|
|
||||||
Name *string `mapstructure:"name" required:"true" cty:"name" hcl:"name"`
|
|
||||||
ReplicaCount *int32 `mapstructure:"replicas" cty:"replicas" hcl:"replicas"`
|
|
||||||
StorageAccountType *string `mapstructure:"storage_account_type" cty:"storage_account_type" hcl:"storage_account_type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatTargetRegion.
|
|
||||||
// FlatTargetRegion is an auto-generated flat version of TargetRegion.
|
|
||||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
func (*TargetRegion) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
|
||||||
return new(FlatTargetRegion)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HCL2Spec returns the hcl spec of a TargetRegion.
|
|
||||||
// This spec is used by HCL to read the fields of TargetRegion.
|
|
||||||
// The decoded values from this spec will then be applied to a FlatTargetRegion.
|
|
||||||
func (*FlatTargetRegion) HCL2Spec() map[string]hcldec.Spec {
|
|
||||||
s := map[string]hcldec.Spec{
|
|
||||||
"name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false},
|
|
||||||
"replicas": &hcldec.AttrSpec{Name: "replicas", Type: cty.Number, Required: false},
|
|
||||||
"storage_account_type": &hcldec.AttrSpec{Name: "storage_account_type", Type: cty.String, Required: false},
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
|
@ -1,158 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSharedImageGalleryDestination_ResourceID(t *testing.T) {
|
|
||||||
sigd := SharedImageGalleryDestination{
|
|
||||||
ResourceGroup: "ResourceGroup",
|
|
||||||
GalleryName: "GalleryName",
|
|
||||||
ImageName: "ImageName",
|
|
||||||
ImageVersion: "ImageVersion",
|
|
||||||
}
|
|
||||||
want := "/subscriptions/SubscriptionID/resourceGroups/ResourceGroup/providers/Microsoft.Compute/galleries/GalleryName/images/ImageName/versions/ImageVersion"
|
|
||||||
if got := sigd.ResourceID("SubscriptionID"); !strings.EqualFold(got, want) {
|
|
||||||
t.Errorf("SharedImageGalleryDestination.ResourceID() = %v, want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSharedImageGalleryDestination_Validate(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
ResourceGroup string
|
|
||||||
GalleryName string
|
|
||||||
ImageName string
|
|
||||||
ImageVersion string
|
|
||||||
TargetRegions []TargetRegion
|
|
||||||
ExcludeFromLatest bool
|
|
||||||
ExcludeFromLatestTypo bool
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
wantErrs []string
|
|
||||||
wantWarns []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "complete",
|
|
||||||
fields: fields{
|
|
||||||
ResourceGroup: "ResourceGroup",
|
|
||||||
GalleryName: "GalleryName",
|
|
||||||
ImageName: "ImageName",
|
|
||||||
ImageVersion: "0.1.2",
|
|
||||||
TargetRegions: []TargetRegion{
|
|
||||||
TargetRegion{
|
|
||||||
Name: "region1",
|
|
||||||
ReplicaCount: 5,
|
|
||||||
StorageAccountType: "Standard_ZRS",
|
|
||||||
},
|
|
||||||
TargetRegion{
|
|
||||||
Name: "region2",
|
|
||||||
ReplicaCount: 3,
|
|
||||||
StorageAccountType: "Standard_LRS",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExcludeFromLatest: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "warn if target regions not specified",
|
|
||||||
fields: fields{
|
|
||||||
ResourceGroup: "ResourceGroup",
|
|
||||||
GalleryName: "GalleryName",
|
|
||||||
ImageName: "ImageName",
|
|
||||||
ImageVersion: "0.1.2",
|
|
||||||
},
|
|
||||||
wantWarns: []string{"sigdest.target_regions is empty; image will only be available in the region of the gallery"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "warn if using exlude_from_latest",
|
|
||||||
fields: fields{
|
|
||||||
ResourceGroup: "ResourceGroup",
|
|
||||||
GalleryName: "GalleryName",
|
|
||||||
ImageName: "ImageName",
|
|
||||||
ImageVersion: "0.1.2",
|
|
||||||
TargetRegions: []TargetRegion{
|
|
||||||
TargetRegion{
|
|
||||||
Name: "region1",
|
|
||||||
ReplicaCount: 5,
|
|
||||||
StorageAccountType: "Standard_ZRS",
|
|
||||||
},
|
|
||||||
TargetRegion{
|
|
||||||
Name: "region2",
|
|
||||||
ReplicaCount: 3,
|
|
||||||
StorageAccountType: "Standard_LRS",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExcludeFromLatestTypo: true,
|
|
||||||
},
|
|
||||||
wantWarns: []string{"sigdest.exlude_from_latest is being deprecated, please use exclude_from_latest"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "version format",
|
|
||||||
wantErrs: []string{
|
|
||||||
"sigdest.image_version should match '^[0-9]+\\.[0-9]+\\.[0-9]+$'",
|
|
||||||
},
|
|
||||||
fields: fields{
|
|
||||||
ResourceGroup: "ResourceGroup",
|
|
||||||
GalleryName: "GalleryName",
|
|
||||||
ImageName: "ImageName",
|
|
||||||
ImageVersion: "0.1.2alpha",
|
|
||||||
TargetRegions: []TargetRegion{
|
|
||||||
TargetRegion{
|
|
||||||
Name: "region1",
|
|
||||||
ReplicaCount: 5,
|
|
||||||
StorageAccountType: "Standard_ZRS",
|
|
||||||
},
|
|
||||||
TargetRegion{
|
|
||||||
Name: "region2",
|
|
||||||
ReplicaCount: 3,
|
|
||||||
StorageAccountType: "Standard_LRS",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExcludeFromLatest: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "required fields",
|
|
||||||
wantErrs: []string{
|
|
||||||
"sigdest.resource_group is required",
|
|
||||||
"sigdest.gallery_name is required",
|
|
||||||
"sigdest.image_name is required",
|
|
||||||
"sigdest.image_version should match '^[0-9]+\\.[0-9]+\\.[0-9]+$'",
|
|
||||||
},
|
|
||||||
wantWarns: []string{"sigdest.target_regions is empty; image will only be available in the region of the gallery"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
sigd := &SharedImageGalleryDestination{
|
|
||||||
ResourceGroup: tt.fields.ResourceGroup,
|
|
||||||
GalleryName: tt.fields.GalleryName,
|
|
||||||
ImageName: tt.fields.ImageName,
|
|
||||||
ImageVersion: tt.fields.ImageVersion,
|
|
||||||
TargetRegions: tt.fields.TargetRegions,
|
|
||||||
ExcludeFromLatest: tt.fields.ExcludeFromLatest,
|
|
||||||
ExcludeFromLatestTypo: tt.fields.ExcludeFromLatestTypo,
|
|
||||||
}
|
|
||||||
gotErrs, gotWarns := sigd.Validate("sigdest")
|
|
||||||
|
|
||||||
var gotStrErrs []string
|
|
||||||
if gotErrs != nil {
|
|
||||||
gotStrErrs = make([]string, len(gotErrs))
|
|
||||||
for i, e := range gotErrs {
|
|
||||||
gotStrErrs[i] = e.Error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(gotStrErrs, tt.wantErrs) {
|
|
||||||
t.Errorf("SharedImageGalleryDestination.Validate() gotErrs = %q, want %q", gotStrErrs, tt.wantErrs)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(gotWarns, tt.wantWarns) {
|
|
||||||
t.Errorf("SharedImageGalleryDestination.Validate() gotWarns = %q, want %q", gotWarns, tt.wantWarns)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ multistep.Step = &StepAttachDisk{}
|
|
||||||
|
|
||||||
type StepAttachDisk struct {
|
|
||||||
attached bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepAttachDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
azcli := state.Get("azureclient").(client.AzureClientSet)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
diskset := state.Get(stateBagKey_Diskset).(Diskset)
|
|
||||||
diskResourceID := diskset.OS().String()
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Attaching disk '%s'", diskResourceID))
|
|
||||||
|
|
||||||
da := NewDiskAttacher(azcli)
|
|
||||||
lun, err := da.AttachDisk(ctx, diskResourceID)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("StepAttachDisk.Run: error: %+v", err)
|
|
||||||
err := fmt.Errorf(
|
|
||||||
"error attaching disk '%s': %v", diskResourceID, err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Say("Disk attached, waiting for device to show up")
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Minute*3) // in case is not configured correctly
|
|
||||||
defer cancel()
|
|
||||||
device, err := da.WaitForDevice(ctx, lun)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("StepAttachDisk.Run: error: %+v", err)
|
|
||||||
err := fmt.Errorf(
|
|
||||||
"error attaching disk '%s': %v", diskResourceID, err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Disk available at %q", device))
|
|
||||||
s.attached = true
|
|
||||||
state.Put("device", device)
|
|
||||||
state.Put("attach_cleanup", s)
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepAttachDisk) Cleanup(state multistep.StateBag) {
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
if err := s.CleanupFunc(state); err != nil {
|
|
||||||
ui.Error(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepAttachDisk) CleanupFunc(state multistep.StateBag) error {
|
|
||||||
|
|
||||||
if s.attached {
|
|
||||||
azcli := state.Get("azureclient").(client.AzureClientSet)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
diskset := state.Get(stateBagKey_Diskset).(Diskset)
|
|
||||||
diskResourceID := diskset.OS().String()
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Detaching disk '%s'", diskResourceID))
|
|
||||||
|
|
||||||
da := NewDiskAttacher(azcli)
|
|
||||||
err := da.DetachDisk(context.Background(), diskResourceID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error detaching %q: %v", diskResourceID, err)
|
|
||||||
}
|
|
||||||
s.attached = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepAttachDisk_Run(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
GetDiskResponseCode int
|
|
||||||
GetDiskResponseBody string
|
|
||||||
|
|
||||||
attachError error
|
|
||||||
waitForDeviceError error
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
want multistep.StepAction
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "HappyPath",
|
|
||||||
want: multistep.ActionContinue,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "AttachError",
|
|
||||||
fields: fields{
|
|
||||||
attachError: errors.New("unit test"),
|
|
||||||
},
|
|
||||||
want: multistep.ActionHalt,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "WaitForDeviceError",
|
|
||||||
fields: fields{
|
|
||||||
waitForDeviceError: errors.New("unit test"),
|
|
||||||
},
|
|
||||||
want: multistep.ActionHalt,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
s := &StepAttachDisk{}
|
|
||||||
|
|
||||||
NewDiskAttacher = func(azcli client.AzureClientSet) DiskAttacher {
|
|
||||||
return &fakeDiskAttacher{
|
|
||||||
attachError: tt.fields.attachError,
|
|
||||||
waitForDeviceError: tt.fields.waitForDeviceError,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dm := compute.NewDisksClient("subscriptionId")
|
|
||||||
dm.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
|
||||||
return &http.Response{
|
|
||||||
Request: r,
|
|
||||||
Body: ioutil.NopCloser(strings.NewReader(tt.fields.GetDiskResponseBody)),
|
|
||||||
StatusCode: tt.fields.GetDiskResponseCode,
|
|
||||||
}, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
errorBuffer := &strings.Builder{}
|
|
||||||
ui := &packersdk.BasicUi{
|
|
||||||
Reader: strings.NewReader(""),
|
|
||||||
Writer: ioutil.Discard,
|
|
||||||
ErrorWriter: errorBuffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("azureclient", &client.AzureClientSetMock{})
|
|
||||||
state.Put("ui", ui)
|
|
||||||
state.Put(stateBagKey_Diskset, diskset("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1"))
|
|
||||||
|
|
||||||
got := s.Run(context.TODO(), state)
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("StepAttachDisk.Run() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
|
|
||||||
if got == multistep.ActionHalt {
|
|
||||||
if _, ok := state.GetOk("error"); !ok {
|
|
||||||
t.Fatal("Expected 'error' to be set in statebag after failure")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type fakeDiskAttacher struct {
|
|
||||||
attachError error
|
|
||||||
waitForDeviceError error
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ DiskAttacher = &fakeDiskAttacher{}
|
|
||||||
|
|
||||||
func (da *fakeDiskAttacher) AttachDisk(ctx context.Context, disk string) (lun int32, err error) {
|
|
||||||
if da.attachError != nil {
|
|
||||||
return 0, da.attachError
|
|
||||||
}
|
|
||||||
return 3, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (da *fakeDiskAttacher) DiskPathForLun(lun int32) string {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (da *fakeDiskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) {
|
|
||||||
if da.waitForDeviceError != nil {
|
|
||||||
return "", da.waitForDeviceError
|
|
||||||
}
|
|
||||||
if lun == 3 {
|
|
||||||
return "/dev/sdq", nil
|
|
||||||
}
|
|
||||||
panic("expected lun==3")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (da *fakeDiskAttacher) DetachDisk(ctx context.Context, disk string) (err error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (da *fakeDiskAttacher) WaitForDetach(ctx context.Context, diskID string) error {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
|
||||||
"github.com/Azure/go-autorest/autorest/to"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ multistep.Step = &StepCreateImage{}
|
|
||||||
|
|
||||||
type StepCreateImage struct {
|
|
||||||
ImageResourceID string
|
|
||||||
ImageOSState string
|
|
||||||
OSDiskStorageAccountType string
|
|
||||||
OSDiskCacheType string
|
|
||||||
DataDiskStorageAccountType string
|
|
||||||
DataDiskCacheType string
|
|
||||||
Location string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
azcli := state.Get("azureclient").(client.AzureClientSet)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
diskset := state.Get(stateBagKey_Diskset).(Diskset)
|
|
||||||
diskResourceID := diskset.OS().String()
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Creating image %s\n using %s for os disk.",
|
|
||||||
s.ImageResourceID,
|
|
||||||
diskResourceID))
|
|
||||||
|
|
||||||
imageResource, err := azure.ParseResourceID(s.ImageResourceID)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("StepCreateImage.Run: error: %+v", err)
|
|
||||||
err := fmt.Errorf(
|
|
||||||
"error parsing image resource id '%s': %v", s.ImageResourceID, err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
image := compute.Image{
|
|
||||||
Location: to.StringPtr(s.Location),
|
|
||||||
ImageProperties: &compute.ImageProperties{
|
|
||||||
StorageProfile: &compute.ImageStorageProfile{
|
|
||||||
OsDisk: &compute.ImageOSDisk{
|
|
||||||
OsState: compute.OperatingSystemStateTypes(s.ImageOSState),
|
|
||||||
OsType: compute.Linux,
|
|
||||||
ManagedDisk: &compute.SubResource{
|
|
||||||
ID: &diskResourceID,
|
|
||||||
},
|
|
||||||
StorageAccountType: compute.StorageAccountTypes(s.OSDiskStorageAccountType),
|
|
||||||
Caching: compute.CachingTypes(s.OSDiskCacheType),
|
|
||||||
},
|
|
||||||
// DataDisks: nil,
|
|
||||||
// ZoneResilient: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Tags: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
var datadisks []compute.ImageDataDisk
|
|
||||||
for lun, resource := range diskset {
|
|
||||||
if lun != -1 {
|
|
||||||
ui.Say(fmt.Sprintf(" using %q for data disk (lun %d).", resource, lun))
|
|
||||||
|
|
||||||
datadisks = append(datadisks, compute.ImageDataDisk{
|
|
||||||
Lun: to.Int32Ptr(lun),
|
|
||||||
ManagedDisk: &compute.SubResource{ID: to.StringPtr(resource.String())},
|
|
||||||
StorageAccountType: compute.StorageAccountTypes(s.DataDiskStorageAccountType),
|
|
||||||
Caching: compute.CachingTypes(s.DataDiskCacheType),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if datadisks != nil {
|
|
||||||
sort.Slice(datadisks, func(i, j int) bool {
|
|
||||||
return *datadisks[i].Lun < *datadisks[j].Lun
|
|
||||||
})
|
|
||||||
image.ImageProperties.StorageProfile.DataDisks = &datadisks
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := azcli.ImagesClient().CreateOrUpdate(
|
|
||||||
ctx,
|
|
||||||
imageResource.ResourceGroup,
|
|
||||||
imageResource.ResourceName,
|
|
||||||
image)
|
|
||||||
if err == nil {
|
|
||||||
log.Println("Image creation in process...")
|
|
||||||
err = f.WaitForCompletionRef(ctx, azcli.PollClient())
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("StepCreateImage.Run: error: %+v", err)
|
|
||||||
err := fmt.Errorf(
|
|
||||||
"error creating image '%s': %v", s.ImageResourceID, err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
log.Printf("Image creation complete: %s", f.Status())
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepCreateImage) Cleanup(bag multistep.StateBag) {} // this is the final artifact, don't delete
|
|
|
@ -1,137 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepCreateImage_Run(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
ImageResourceID string
|
|
||||||
ImageOSState string
|
|
||||||
OSDiskStorageAccountType string
|
|
||||||
OSDiskCacheType string
|
|
||||||
DataDiskStorageAccountType string
|
|
||||||
DataDiskCacheType string
|
|
||||||
Location string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
diskset Diskset
|
|
||||||
want multistep.StepAction
|
|
||||||
wantPutBody string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "happy path",
|
|
||||||
fields: fields{
|
|
||||||
ImageResourceID: "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/images/myImage",
|
|
||||||
Location: "location1",
|
|
||||||
OSDiskStorageAccountType: "Standard_LRS",
|
|
||||||
OSDiskCacheType: "ReadWrite",
|
|
||||||
DataDiskStorageAccountType: "Premium_LRS",
|
|
||||||
DataDiskCacheType: "ReadOnly",
|
|
||||||
},
|
|
||||||
diskset: diskset(
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/osdisk",
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk0",
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk1",
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk2"),
|
|
||||||
want: multistep.ActionContinue,
|
|
||||||
wantPutBody: `{
|
|
||||||
"location": "location1",
|
|
||||||
"properties": {
|
|
||||||
"storageProfile": {
|
|
||||||
"osDisk": {
|
|
||||||
"osType": "Linux",
|
|
||||||
"managedDisk": {
|
|
||||||
"id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/osdisk"
|
|
||||||
},
|
|
||||||
"caching": "ReadWrite",
|
|
||||||
"storageAccountType": "Standard_LRS"
|
|
||||||
},
|
|
||||||
"dataDisks": [
|
|
||||||
{
|
|
||||||
"lun": 0,
|
|
||||||
"managedDisk": {
|
|
||||||
"id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk0"
|
|
||||||
},
|
|
||||||
"caching": "ReadOnly",
|
|
||||||
"storageAccountType": "Premium_LRS"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"lun": 1,
|
|
||||||
"managedDisk": {
|
|
||||||
"id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk1"
|
|
||||||
},
|
|
||||||
"caching": "ReadOnly",
|
|
||||||
"storageAccountType": "Premium_LRS"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"lun": 2,
|
|
||||||
"managedDisk": {
|
|
||||||
"id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk2"
|
|
||||||
},
|
|
||||||
"caching": "ReadOnly",
|
|
||||||
"storageAccountType": "Premium_LRS"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
|
|
||||||
ic := compute.NewImagesClient("subscriptionID")
|
|
||||||
ic.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
|
||||||
if r.Method != "PUT" {
|
|
||||||
t.Fatal("Expected only a PUT call")
|
|
||||||
}
|
|
||||||
if tt.wantPutBody != "" {
|
|
||||||
b, _ := ioutil.ReadAll(r.Body)
|
|
||||||
expectedPutBody := regexp.MustCompile(`[\s\n]`).ReplaceAllString(tt.wantPutBody, "")
|
|
||||||
if string(b) != expectedPutBody {
|
|
||||||
t.Errorf("expected body to be %v, but got %v", expectedPutBody, string(b))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &http.Response{
|
|
||||||
Request: r,
|
|
||||||
StatusCode: 200,
|
|
||||||
}, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("azureclient", &client.AzureClientSetMock{
|
|
||||||
ImagesClientMock: ic,
|
|
||||||
})
|
|
||||||
state.Put("ui", packersdk.TestUi(t))
|
|
||||||
state.Put(stateBagKey_Diskset, tt.diskset)
|
|
||||||
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
s := &StepCreateImage{
|
|
||||||
ImageResourceID: tt.fields.ImageResourceID,
|
|
||||||
ImageOSState: tt.fields.ImageOSState,
|
|
||||||
OSDiskStorageAccountType: tt.fields.OSDiskStorageAccountType,
|
|
||||||
OSDiskCacheType: tt.fields.OSDiskCacheType,
|
|
||||||
DataDiskStorageAccountType: tt.fields.DataDiskStorageAccountType,
|
|
||||||
DataDiskCacheType: tt.fields.DataDiskCacheType,
|
|
||||||
Location: tt.fields.Location,
|
|
||||||
}
|
|
||||||
if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("StepCreateImage.Run() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,239 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest/to"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ multistep.Step = &StepCreateNewDiskset{}
|
|
||||||
|
|
||||||
type StepCreateNewDiskset struct {
|
|
||||||
OSDiskID string // Disk ID
|
|
||||||
OSDiskSizeGB int32 // optional, ignored if 0
|
|
||||||
OSDiskStorageAccountType string // from compute.DiskStorageAccountTypes
|
|
||||||
DataDiskStorageAccountType string // from compute.DiskStorageAccountTypes
|
|
||||||
|
|
||||||
DataDiskIDPrefix string
|
|
||||||
|
|
||||||
disks Diskset
|
|
||||||
|
|
||||||
HyperVGeneration string // For OS disk
|
|
||||||
|
|
||||||
// Copy another disk
|
|
||||||
SourceOSDiskResourceID string
|
|
||||||
|
|
||||||
// Extract from platform image
|
|
||||||
SourcePlatformImage *client.PlatformImage
|
|
||||||
// Extract from shared image
|
|
||||||
SourceImageResourceID string
|
|
||||||
// Location is needed for platform and shared images
|
|
||||||
Location string
|
|
||||||
|
|
||||||
SkipCleanup bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCreateNewDiskset) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
azcli := state.Get("azureclient").(client.AzureClientSet)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
|
|
||||||
s.disks = make(Diskset)
|
|
||||||
|
|
||||||
errorMessage := func(format string, params ...interface{}) multistep.StepAction {
|
|
||||||
err := fmt.Errorf("StepCreateNewDiskset.Run: error: "+format, params...)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
// we always have an OS disk
|
|
||||||
osDisk, err := client.ParseResourceID(s.OSDiskID)
|
|
||||||
if err != nil {
|
|
||||||
return errorMessage("error parsing resource id '%s': %v", s.OSDiskID, err)
|
|
||||||
}
|
|
||||||
if !strings.EqualFold(osDisk.Provider, "Microsoft.Compute") ||
|
|
||||||
!strings.EqualFold(osDisk.ResourceType.String(), "disks") {
|
|
||||||
return errorMessage("Resource %q is not of type Microsoft.Compute/disks", s.OSDiskID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// transform step config to disk model
|
|
||||||
disk := s.getOSDiskDefinition(azcli.SubscriptionID())
|
|
||||||
|
|
||||||
// Initiate disk creation
|
|
||||||
f, err := azcli.DisksClient().CreateOrUpdate(ctx, osDisk.ResourceGroup, osDisk.ResourceName.String(), disk)
|
|
||||||
if err != nil {
|
|
||||||
return errorMessage("Failed to initiate resource creation: %q", osDisk)
|
|
||||||
}
|
|
||||||
s.disks[-1] = osDisk // save the resoure we just create in our disk set
|
|
||||||
state.Put(stateBagKey_Diskset, s.disks) // update the statebag
|
|
||||||
ui.Say(fmt.Sprintf("Creating disk %q", s.OSDiskID))
|
|
||||||
|
|
||||||
type Future struct {
|
|
||||||
client.Resource
|
|
||||||
compute.DisksCreateOrUpdateFuture
|
|
||||||
}
|
|
||||||
futures := []Future{{osDisk, f}}
|
|
||||||
|
|
||||||
if s.SourceImageResourceID != "" {
|
|
||||||
// retrieve image to see if there are any datadisks
|
|
||||||
imageID, err := client.ParseResourceID(s.SourceImageResourceID)
|
|
||||||
if err != nil {
|
|
||||||
return errorMessage("could not parse source image id %q: %v", s.SourceImageResourceID, err)
|
|
||||||
}
|
|
||||||
if !strings.EqualFold(imageID.Provider+"/"+imageID.ResourceType.String(),
|
|
||||||
"Microsoft.Compute/galleries/images/versions") {
|
|
||||||
return errorMessage("source image id is not a shared image version %q, expected type 'Microsoft.Compute/galleries/images/versions'", imageID)
|
|
||||||
}
|
|
||||||
image, err := azcli.GalleryImageVersionsClient().Get(ctx,
|
|
||||||
imageID.ResourceGroup,
|
|
||||||
imageID.ResourceName[0], imageID.ResourceName[1], imageID.ResourceName[2], "")
|
|
||||||
if err != nil {
|
|
||||||
return errorMessage("error retrieving source image %q: %v", imageID, err)
|
|
||||||
}
|
|
||||||
if image.GalleryImageVersionProperties != nil &&
|
|
||||||
image.GalleryImageVersionProperties.StorageProfile != nil &&
|
|
||||||
image.GalleryImageVersionProperties.StorageProfile.DataDiskImages != nil {
|
|
||||||
for i, ddi := range *image.GalleryImageVersionProperties.StorageProfile.DataDiskImages {
|
|
||||||
if ddi.Lun == nil {
|
|
||||||
return errorMessage("unexpected: lun is null for data disk # %d", i)
|
|
||||||
}
|
|
||||||
datadiskID, err := client.ParseResourceID(fmt.Sprintf("%s%d", s.DataDiskIDPrefix, *ddi.Lun))
|
|
||||||
if err != nil {
|
|
||||||
return errorMessage("unable to construct resource id for datadisk: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
disk := s.getDatadiskDefinitionFromImage(*ddi.Lun)
|
|
||||||
// Initiate disk creation
|
|
||||||
f, err := azcli.DisksClient().CreateOrUpdate(ctx, datadiskID.ResourceGroup, datadiskID.ResourceName.String(), disk)
|
|
||||||
if err != nil {
|
|
||||||
return errorMessage("Failed to initiate resource creation: %q", datadiskID)
|
|
||||||
}
|
|
||||||
s.disks[*ddi.Lun] = datadiskID // save the resoure we just create in our disk set
|
|
||||||
state.Put(stateBagKey_Diskset, s.disks) // update the statebag
|
|
||||||
ui.Say(fmt.Sprintf("Creating disk %q", datadiskID))
|
|
||||||
|
|
||||||
futures = append(futures, Future{datadiskID, f})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Say("Waiting for disks to be created.")
|
|
||||||
|
|
||||||
// Wait for completion
|
|
||||||
for _, f := range futures {
|
|
||||||
cli := azcli.PollClient() // quick polling for quick operations
|
|
||||||
cli.PollingDelay = time.Second
|
|
||||||
err = f.WaitForCompletionRef(ctx, cli)
|
|
||||||
if err != nil {
|
|
||||||
return errorMessage(
|
|
||||||
"error creating new disk '%s': %v", f.Resource, err)
|
|
||||||
}
|
|
||||||
ui.Say(fmt.Sprintf("Disk %q created", f.Resource))
|
|
||||||
}
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s StepCreateNewDiskset) getOSDiskDefinition(subscriptionID string) compute.Disk {
|
|
||||||
disk := compute.Disk{
|
|
||||||
Location: to.StringPtr(s.Location),
|
|
||||||
DiskProperties: &compute.DiskProperties{
|
|
||||||
OsType: "Linux",
|
|
||||||
CreationData: &compute.CreationData{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.OSDiskStorageAccountType != "" {
|
|
||||||
disk.Sku = &compute.DiskSku{
|
|
||||||
Name: compute.DiskStorageAccountTypes(s.OSDiskStorageAccountType),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.HyperVGeneration != "" {
|
|
||||||
disk.DiskProperties.HyperVGeneration = compute.HyperVGeneration(s.HyperVGeneration)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.OSDiskSizeGB > 0 {
|
|
||||||
disk.DiskProperties.DiskSizeGB = to.Int32Ptr(s.OSDiskSizeGB)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case s.SourcePlatformImage != nil:
|
|
||||||
disk.CreationData.CreateOption = compute.FromImage
|
|
||||||
disk.CreationData.ImageReference = &compute.ImageDiskReference{
|
|
||||||
ID: to.StringPtr(fmt.Sprintf(
|
|
||||||
"/subscriptions/%s/providers/Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions/%s",
|
|
||||||
subscriptionID, s.Location,
|
|
||||||
s.SourcePlatformImage.Publisher, s.SourcePlatformImage.Offer, s.SourcePlatformImage.Sku, s.SourcePlatformImage.Version)),
|
|
||||||
}
|
|
||||||
case s.SourceOSDiskResourceID != "":
|
|
||||||
disk.CreationData.CreateOption = compute.Copy
|
|
||||||
disk.CreationData.SourceResourceID = to.StringPtr(s.SourceOSDiskResourceID)
|
|
||||||
case s.SourceImageResourceID != "":
|
|
||||||
disk.CreationData.CreateOption = compute.FromImage
|
|
||||||
disk.CreationData.GalleryImageReference = &compute.ImageDiskReference{
|
|
||||||
ID: to.StringPtr(s.SourceImageResourceID),
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
disk.CreationData.CreateOption = compute.Empty
|
|
||||||
}
|
|
||||||
return disk
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s StepCreateNewDiskset) getDatadiskDefinitionFromImage(lun int32) compute.Disk {
|
|
||||||
disk := compute.Disk{
|
|
||||||
Location: to.StringPtr(s.Location),
|
|
||||||
DiskProperties: &compute.DiskProperties{
|
|
||||||
CreationData: &compute.CreationData{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
disk.CreationData.CreateOption = compute.FromImage
|
|
||||||
disk.CreationData.GalleryImageReference = &compute.ImageDiskReference{
|
|
||||||
ID: to.StringPtr(s.SourceImageResourceID),
|
|
||||||
Lun: to.Int32Ptr(lun),
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.DataDiskStorageAccountType != "" {
|
|
||||||
disk.Sku = &compute.DiskSku{
|
|
||||||
Name: compute.DiskStorageAccountTypes(s.DataDiskStorageAccountType),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return disk
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCreateNewDiskset) Cleanup(state multistep.StateBag) {
|
|
||||||
if !s.SkipCleanup {
|
|
||||||
azcli := state.Get("azureclient").(client.AzureClientSet)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
|
|
||||||
for _, d := range s.disks {
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Waiting for disk %q detach to complete", d))
|
|
||||||
err := NewDiskAttacher(azcli).WaitForDetach(context.Background(), d.String())
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf("error detaching disk %q: %s", d, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Deleting disk %q", d))
|
|
||||||
|
|
||||||
f, err := azcli.DisksClient().Delete(context.TODO(), d.ResourceGroup, d.ResourceName.String())
|
|
||||||
if err == nil {
|
|
||||||
err = f.WaitForCompletionRef(context.TODO(), azcli.PollClient())
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("StepCreateNewDiskset.Cleanup: error: %+v", err)
|
|
||||||
ui.Error(fmt.Sprintf("error deleting disk '%s': %v.", d, err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,247 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepCreateNewDisk_Run(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields StepCreateNewDiskset
|
|
||||||
expectedPutDiskBodies []string
|
|
||||||
want multistep.StepAction
|
|
||||||
verifyDiskset *Diskset
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "from disk",
|
|
||||||
fields: StepCreateNewDiskset{
|
|
||||||
OSDiskID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName",
|
|
||||||
OSDiskSizeGB: 42,
|
|
||||||
OSDiskStorageAccountType: string(compute.PremiumLRS),
|
|
||||||
HyperVGeneration: string(compute.V1),
|
|
||||||
Location: "westus",
|
|
||||||
SourceOSDiskResourceID: "SourceDisk",
|
|
||||||
},
|
|
||||||
expectedPutDiskBodies: []string{`
|
|
||||||
{
|
|
||||||
"location": "westus",
|
|
||||||
"properties": {
|
|
||||||
"osType": "Linux",
|
|
||||||
"hyperVGeneration": "V1",
|
|
||||||
"creationData": {
|
|
||||||
"createOption": "Copy",
|
|
||||||
"sourceResourceId": "SourceDisk"
|
|
||||||
},
|
|
||||||
"diskSizeGB": 42
|
|
||||||
},
|
|
||||||
"sku": {
|
|
||||||
"name": "Premium_LRS"
|
|
||||||
}
|
|
||||||
}`},
|
|
||||||
want: multistep.ActionContinue,
|
|
||||||
verifyDiskset: &Diskset{-1: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName")},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "from platform image",
|
|
||||||
fields: StepCreateNewDiskset{
|
|
||||||
OSDiskID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName",
|
|
||||||
OSDiskStorageAccountType: string(compute.StandardLRS),
|
|
||||||
HyperVGeneration: string(compute.V1),
|
|
||||||
Location: "westus",
|
|
||||||
SourcePlatformImage: &client.PlatformImage{
|
|
||||||
Publisher: "Microsoft",
|
|
||||||
Offer: "Windows",
|
|
||||||
Sku: "2016-DataCenter",
|
|
||||||
Version: "2016.1.4",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedPutDiskBodies: []string{`
|
|
||||||
{
|
|
||||||
"location": "westus",
|
|
||||||
"properties": {
|
|
||||||
"osType": "Linux",
|
|
||||||
"hyperVGeneration": "V1",
|
|
||||||
"creationData": {
|
|
||||||
"createOption":"FromImage",
|
|
||||||
"imageReference": {
|
|
||||||
"id":"/subscriptions/SubscriptionID/providers/Microsoft.Compute/locations/westus/publishers/Microsoft/artifacttypes/vmimage/offers/Windows/skus/2016-DataCenter/versions/2016.1.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sku": {
|
|
||||||
"name": "Standard_LRS"
|
|
||||||
}
|
|
||||||
}`},
|
|
||||||
want: multistep.ActionContinue,
|
|
||||||
verifyDiskset: &Diskset{-1: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName")},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "from shared image",
|
|
||||||
fields: StepCreateNewDiskset{
|
|
||||||
OSDiskID: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName",
|
|
||||||
OSDiskStorageAccountType: string(compute.StandardLRS),
|
|
||||||
DataDiskStorageAccountType: string(compute.PremiumLRS),
|
|
||||||
DataDiskIDPrefix: "/subscriptions/SubscriptionID/resourcegroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryDataDisk-",
|
|
||||||
HyperVGeneration: string(compute.V1),
|
|
||||||
Location: "westus",
|
|
||||||
SourceImageResourceID: "/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3",
|
|
||||||
},
|
|
||||||
|
|
||||||
expectedPutDiskBodies: []string{`
|
|
||||||
{
|
|
||||||
"location": "westus",
|
|
||||||
"properties": {
|
|
||||||
"osType": "Linux",
|
|
||||||
"hyperVGeneration": "V1",
|
|
||||||
"creationData": {
|
|
||||||
"createOption":"FromImage",
|
|
||||||
"galleryImageReference": {
|
|
||||||
"id":"/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sku": {
|
|
||||||
"name": "Standard_LRS"
|
|
||||||
}
|
|
||||||
}`, `
|
|
||||||
{
|
|
||||||
"location": "westus",
|
|
||||||
"properties": {
|
|
||||||
"creationData": {
|
|
||||||
"createOption":"FromImage",
|
|
||||||
"galleryImageReference": {
|
|
||||||
"id": "/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3",
|
|
||||||
"lun": 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sku": {
|
|
||||||
"name": "Premium_LRS"
|
|
||||||
}
|
|
||||||
}`, `
|
|
||||||
{
|
|
||||||
"location": "westus",
|
|
||||||
"properties": {
|
|
||||||
"creationData": {
|
|
||||||
"createOption":"FromImage",
|
|
||||||
"galleryImageReference": {
|
|
||||||
"id": "/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3",
|
|
||||||
"lun": 9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sku": {
|
|
||||||
"name": "Premium_LRS"
|
|
||||||
}
|
|
||||||
}`, `
|
|
||||||
{
|
|
||||||
"location": "westus",
|
|
||||||
"properties": {
|
|
||||||
"creationData": {
|
|
||||||
"createOption":"FromImage",
|
|
||||||
"galleryImageReference": {
|
|
||||||
"id": "/subscriptions/SubscriptionID/resourcegroups/imagegroup/providers/Microsoft.Compute/galleries/MyGallery/images/MyImage/versions/1.2.3",
|
|
||||||
"lun": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sku": {
|
|
||||||
"name": "Premium_LRS"
|
|
||||||
}
|
|
||||||
}`},
|
|
||||||
want: multistep.ActionContinue,
|
|
||||||
verifyDiskset: &Diskset{
|
|
||||||
-1: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryOSDiskName"),
|
|
||||||
3: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryDataDisk-3"),
|
|
||||||
5: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryDataDisk-5"),
|
|
||||||
9: resource("/subscriptions/SubscriptionID/resourceGroups/ResourceGroupName/providers/Microsoft.Compute/disks/TemporaryDataDisk-9"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
s := tt.fields
|
|
||||||
|
|
||||||
bodyCount := 0
|
|
||||||
m := compute.NewDisksClient("SubscriptionID")
|
|
||||||
m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
|
||||||
if r.Method != "PUT" {
|
|
||||||
t.Fatal("Expected only a PUT disk call")
|
|
||||||
}
|
|
||||||
b, _ := ioutil.ReadAll(r.Body)
|
|
||||||
expectedPutDiskBody := regexp.MustCompile(`[\s\n]`).ReplaceAllString(tt.expectedPutDiskBodies[bodyCount], "")
|
|
||||||
bodyCount++
|
|
||||||
if string(b) != expectedPutDiskBody {
|
|
||||||
t.Fatalf("expected body #%d to be %q, but got %q", bodyCount, expectedPutDiskBody, string(b))
|
|
||||||
}
|
|
||||||
return &http.Response{
|
|
||||||
Request: r,
|
|
||||||
StatusCode: 200,
|
|
||||||
}, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
giv := compute.NewGalleryImageVersionsClient("SubscriptionID")
|
|
||||||
giv.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
|
||||||
if r.Method == "GET" &&
|
|
||||||
regexp.MustCompile(`(?i)/versions/1\.2\.3$`).MatchString(r.URL.Path) {
|
|
||||||
return &http.Response{
|
|
||||||
Request: r,
|
|
||||||
Body: ioutil.NopCloser(strings.NewReader(`{
|
|
||||||
"properties": { "storageProfile": {
|
|
||||||
"dataDiskImages":[
|
|
||||||
{ "lun": 5 },
|
|
||||||
{ "lun": 9 },
|
|
||||||
{ "lun": 3 }
|
|
||||||
]
|
|
||||||
} }
|
|
||||||
}`)),
|
|
||||||
StatusCode: 200,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return &http.Response{
|
|
||||||
Request: r,
|
|
||||||
Status: "Unexpected request",
|
|
||||||
StatusCode: 500,
|
|
||||||
}, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("azureclient", &client.AzureClientSetMock{
|
|
||||||
SubscriptionIDMock: "SubscriptionID",
|
|
||||||
DisksClientMock: m,
|
|
||||||
GalleryImageVersionsClientMock: giv,
|
|
||||||
})
|
|
||||||
state.Put("ui", packersdk.TestUi(t))
|
|
||||||
|
|
||||||
if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("StepCreateNewDisk.Run() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
|
|
||||||
ds := state.Get(stateBagKey_Diskset)
|
|
||||||
if tt.verifyDiskset != nil && !reflect.DeepEqual(*tt.verifyDiskset, ds) {
|
|
||||||
t.Errorf("Error verifying diskset after Run(), got %v, want %v", ds, *tt.verifyDiskset)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resource(id string) client.Resource {
|
|
||||||
v, err := client.ParseResourceID(id)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"sort"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest/to"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StepCreateSharedImageVersion struct {
|
|
||||||
Destination SharedImageGalleryDestination
|
|
||||||
OSDiskCacheType string
|
|
||||||
DataDiskCacheType string
|
|
||||||
Location string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCreateSharedImageVersion) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
azcli := state.Get("azureclient").(client.AzureClientSet)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
snapshotset := state.Get(stateBagKey_Snapshotset).(Diskset)
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Creating image version %s\n using %q for os disk.",
|
|
||||||
s.Destination.ResourceID(azcli.SubscriptionID()),
|
|
||||||
snapshotset.OS()))
|
|
||||||
|
|
||||||
var targetRegions []compute.TargetRegion
|
|
||||||
// transform target regions to API objects
|
|
||||||
for _, tr := range s.Destination.TargetRegions {
|
|
||||||
apiObject := compute.TargetRegion{
|
|
||||||
Name: to.StringPtr(tr.Name),
|
|
||||||
RegionalReplicaCount: to.Int32Ptr(tr.ReplicaCount),
|
|
||||||
StorageAccountType: compute.StorageAccountType(tr.StorageAccountType),
|
|
||||||
}
|
|
||||||
targetRegions = append(targetRegions, apiObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
imageVersion := compute.GalleryImageVersion{
|
|
||||||
Location: to.StringPtr(s.Location),
|
|
||||||
GalleryImageVersionProperties: &compute.GalleryImageVersionProperties{
|
|
||||||
StorageProfile: &compute.GalleryImageVersionStorageProfile{
|
|
||||||
OsDiskImage: &compute.GalleryOSDiskImage{
|
|
||||||
Source: &compute.GalleryArtifactVersionSource{ID: to.StringPtr(snapshotset.OS().String())},
|
|
||||||
HostCaching: compute.HostCaching(s.OSDiskCacheType),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PublishingProfile: &compute.GalleryImageVersionPublishingProfile{
|
|
||||||
TargetRegions: &targetRegions,
|
|
||||||
ExcludeFromLatest: to.BoolPtr(s.Destination.ExcludeFromLatest),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var datadisks []compute.GalleryDataDiskImage
|
|
||||||
for lun, resource := range snapshotset {
|
|
||||||
if lun != -1 {
|
|
||||||
ui.Say(fmt.Sprintf(" using %q for data disk (lun %d).", resource, lun))
|
|
||||||
|
|
||||||
datadisks = append(datadisks, compute.GalleryDataDiskImage{
|
|
||||||
Lun: to.Int32Ptr(lun),
|
|
||||||
Source: &compute.GalleryArtifactVersionSource{ID: to.StringPtr(resource.String())},
|
|
||||||
HostCaching: compute.HostCaching(s.DataDiskCacheType),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if datadisks != nil {
|
|
||||||
// sort by lun
|
|
||||||
sort.Slice(datadisks, func(i, j int) bool {
|
|
||||||
return *datadisks[i].Lun < *datadisks[j].Lun
|
|
||||||
})
|
|
||||||
imageVersion.GalleryImageVersionProperties.StorageProfile.DataDiskImages = &datadisks
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := azcli.GalleryImageVersionsClient().CreateOrUpdate(
|
|
||||||
ctx,
|
|
||||||
s.Destination.ResourceGroup,
|
|
||||||
s.Destination.GalleryName,
|
|
||||||
s.Destination.ImageName,
|
|
||||||
s.Destination.ImageVersion,
|
|
||||||
imageVersion)
|
|
||||||
if err == nil {
|
|
||||||
log.Println("Shared image version creation in process...")
|
|
||||||
pollClient := azcli.PollClient()
|
|
||||||
pollClient.PollingDelay = 10 * time.Second
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Hour*12)
|
|
||||||
defer cancel()
|
|
||||||
err = f.WaitForCompletionRef(ctx, pollClient)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("StepCreateSharedImageVersion.Run: error: %+v", err)
|
|
||||||
err := fmt.Errorf(
|
|
||||||
"error creating shared image version '%s': %v", s.Destination.ResourceID(azcli.SubscriptionID()), err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
log.Printf("Image creation complete: %s", f.Status())
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*StepCreateSharedImageVersion) Cleanup(multistep.StateBag) {}
|
|
|
@ -1,144 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepCreateSharedImageVersion_Run(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Destination SharedImageGalleryDestination
|
|
||||||
OSDiskCacheType string
|
|
||||||
DataDiskCacheType string
|
|
||||||
Location string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
snapshotset Diskset
|
|
||||||
want multistep.StepAction
|
|
||||||
expectedPutBody string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "happy path",
|
|
||||||
fields: fields{
|
|
||||||
Destination: SharedImageGalleryDestination{
|
|
||||||
ResourceGroup: "ResourceGroup",
|
|
||||||
GalleryName: "GalleryName",
|
|
||||||
ImageName: "ImageName",
|
|
||||||
ImageVersion: "0.1.2",
|
|
||||||
TargetRegions: []TargetRegion{
|
|
||||||
{
|
|
||||||
Name: "region1",
|
|
||||||
ReplicaCount: 5,
|
|
||||||
StorageAccountType: "Standard_ZRS",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ExcludeFromLatest: true,
|
|
||||||
},
|
|
||||||
OSDiskCacheType: "ReadWrite",
|
|
||||||
DataDiskCacheType: "None",
|
|
||||||
Location: "region2",
|
|
||||||
},
|
|
||||||
snapshotset: diskset(
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/osdisksnapshot",
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot0",
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot1",
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot2"),
|
|
||||||
expectedPutBody: `{
|
|
||||||
"location": "region2",
|
|
||||||
"properties": {
|
|
||||||
"publishingProfile": {
|
|
||||||
"targetRegions": [
|
|
||||||
{
|
|
||||||
"name": "region1",
|
|
||||||
"regionalReplicaCount": 5,
|
|
||||||
"storageAccountType": "Standard_ZRS"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"excludeFromLatest": true
|
|
||||||
},
|
|
||||||
"storageProfile": {
|
|
||||||
"osDiskImage": {
|
|
||||||
"hostCaching": "ReadWrite",
|
|
||||||
"source": {
|
|
||||||
"id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/osdisksnapshot"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dataDiskImages": [
|
|
||||||
{
|
|
||||||
"lun": 0,
|
|
||||||
"hostCaching": "None",
|
|
||||||
"source": {
|
|
||||||
"id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"lun": 1,
|
|
||||||
"hostCaching": "None",
|
|
||||||
"source": {
|
|
||||||
"id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"lun": 2,
|
|
||||||
"hostCaching": "None",
|
|
||||||
"source": {
|
|
||||||
"id": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/snapshots/datadisksnapshot2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
expectedPutBody := regexp.MustCompile(`[\s\n]`).ReplaceAllString(tt.expectedPutBody, "")
|
|
||||||
|
|
||||||
m := compute.NewGalleryImageVersionsClient("subscriptionId")
|
|
||||||
m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
|
||||||
if r.Method != "PUT" {
|
|
||||||
t.Fatal("Expected only a PUT call")
|
|
||||||
}
|
|
||||||
b, _ := ioutil.ReadAll(r.Body)
|
|
||||||
if string(b) != expectedPutBody {
|
|
||||||
t.Errorf("expected body to be %v, but got %v", expectedPutBody, string(b))
|
|
||||||
}
|
|
||||||
return &http.Response{
|
|
||||||
Request: r,
|
|
||||||
StatusCode: 200,
|
|
||||||
}, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("azureclient", &client.AzureClientSetMock{
|
|
||||||
GalleryImageVersionsClientMock: m,
|
|
||||||
})
|
|
||||||
state.Put("ui", packersdk.TestUi(t))
|
|
||||||
state.Put(stateBagKey_Snapshotset, tt.snapshotset)
|
|
||||||
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
s := &StepCreateSharedImageVersion{
|
|
||||||
Destination: tt.fields.Destination,
|
|
||||||
OSDiskCacheType: tt.fields.OSDiskCacheType,
|
|
||||||
DataDiskCacheType: tt.fields.DataDiskCacheType,
|
|
||||||
Location: tt.fields.Location,
|
|
||||||
}
|
|
||||||
if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("StepCreateSharedImageVersion.Run() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest/to"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ multistep.Step = &StepCreateSnapshotset{}
|
|
||||||
|
|
||||||
type StepCreateSnapshotset struct {
|
|
||||||
OSDiskSnapshotID string
|
|
||||||
DataDiskSnapshotIDPrefix string
|
|
||||||
Location string
|
|
||||||
|
|
||||||
SkipCleanup bool
|
|
||||||
|
|
||||||
snapshots Diskset
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCreateSnapshotset) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
azcli := state.Get("azureclient").(client.AzureClientSet)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
diskset := state.Get(stateBagKey_Diskset).(Diskset)
|
|
||||||
|
|
||||||
s.snapshots = make(Diskset)
|
|
||||||
|
|
||||||
errorMessage := func(format string, params ...interface{}) multistep.StepAction {
|
|
||||||
err := fmt.Errorf("StepCreateSnapshotset.Run: error: "+format, params...)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
for lun, resource := range diskset {
|
|
||||||
snapshotID := fmt.Sprintf("%s%d", s.DataDiskSnapshotIDPrefix, lun)
|
|
||||||
if lun == -1 {
|
|
||||||
snapshotID = s.OSDiskSnapshotID
|
|
||||||
}
|
|
||||||
ssr, err := client.ParseResourceID(snapshotID)
|
|
||||||
if err != nil {
|
|
||||||
errorMessage("Could not create a valid resource id, tried %q: %v", snapshotID, err)
|
|
||||||
}
|
|
||||||
if !strings.EqualFold(ssr.Provider, "Microsoft.Compute") ||
|
|
||||||
!strings.EqualFold(ssr.ResourceType.String(), "snapshots") {
|
|
||||||
return errorMessage("Resource %q is not of type Microsoft.Compute/snapshots", snapshotID)
|
|
||||||
}
|
|
||||||
s.snapshots[lun] = ssr
|
|
||||||
state.Put(stateBagKey_Snapshotset, s.snapshots)
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Creating snapshot %q", ssr))
|
|
||||||
|
|
||||||
snapshot := compute.Snapshot{
|
|
||||||
Location: to.StringPtr(s.Location),
|
|
||||||
SnapshotProperties: &compute.SnapshotProperties{
|
|
||||||
CreationData: &compute.CreationData{
|
|
||||||
CreateOption: compute.Copy,
|
|
||||||
SourceResourceID: to.StringPtr(resource.String()),
|
|
||||||
},
|
|
||||||
Incremental: to.BoolPtr(false),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := azcli.SnapshotsClient().CreateOrUpdate(ctx, ssr.ResourceGroup, ssr.ResourceName.String(), snapshot)
|
|
||||||
if err != nil {
|
|
||||||
return errorMessage("error initiating snapshot %q: %v", ssr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pollClient := azcli.PollClient()
|
|
||||||
pollClient.PollingDelay = 2 * time.Second
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Hour*12)
|
|
||||||
defer cancel()
|
|
||||||
err = f.WaitForCompletionRef(ctx, pollClient)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return errorMessage("error creating snapshot '%s': %v", s.OSDiskSnapshotID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StepCreateSnapshotset) Cleanup(state multistep.StateBag) {
|
|
||||||
if !s.SkipCleanup {
|
|
||||||
azcli := state.Get("azureclient").(client.AzureClientSet)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
|
|
||||||
for _, resource := range s.snapshots {
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Removing any active SAS for snapshot %q", resource))
|
|
||||||
{
|
|
||||||
f, err := azcli.SnapshotsClient().RevokeAccess(context.TODO(), resource.ResourceGroup, resource.ResourceName.String())
|
|
||||||
if err == nil {
|
|
||||||
log.Printf("StepCreateSnapshotset.Cleanup: removing SAS...")
|
|
||||||
err = f.WaitForCompletionRef(context.TODO(), azcli.PollClient())
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("StepCreateSnapshotset.Cleanup: error: %+v", err)
|
|
||||||
ui.Error(fmt.Sprintf("error deleting snapshot %q: %v.", resource, err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Deleting snapshot %q", resource))
|
|
||||||
{
|
|
||||||
f, err := azcli.SnapshotsClient().Delete(context.TODO(), resource.ResourceGroup, resource.ResourceName.String())
|
|
||||||
if err == nil {
|
|
||||||
log.Printf("StepCreateSnapshotset.Cleanup: deleting snapshot...")
|
|
||||||
err = f.WaitForCompletionRef(context.TODO(), azcli.PollClient())
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("StepCreateSnapshotset.Cleanup: error: %+v", err)
|
|
||||||
ui.Error(fmt.Sprintf("error deleting snapshot %q: %v.", resource, err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,230 +0,0 @@
|
||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
|
|
||||||
"github.com/Azure/go-autorest/autorest"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/azure/common/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepCreateSnapshot_Run(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
OSDiskSnapshotID string
|
|
||||||
DataDiskSnapshotIDPrefix string
|
|
||||||
Location string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
diskset Diskset
|
|
||||||
want multistep.StepAction
|
|
||||||
wantSnapshotset Diskset
|
|
||||||
expectedPutBody string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "happy path",
|
|
||||||
fields: fields{
|
|
||||||
OSDiskSnapshotID: "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/osdisk-snap",
|
|
||||||
Location: "region1",
|
|
||||||
},
|
|
||||||
diskset: diskset("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1"),
|
|
||||||
expectedPutBody: `{
|
|
||||||
"location": "region1",
|
|
||||||
"properties": {
|
|
||||||
"creationData": {
|
|
||||||
"createOption": "Copy",
|
|
||||||
"sourceResourceId": "/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1"
|
|
||||||
},
|
|
||||||
"incremental": false
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
wantSnapshotset: diskset("/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/osdisk-snap"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multi disk",
|
|
||||||
fields: fields{
|
|
||||||
OSDiskSnapshotID: "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/osdisk-snap",
|
|
||||||
DataDiskSnapshotIDPrefix: "/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datadisk-snap",
|
|
||||||
Location: "region1",
|
|
||||||
},
|
|
||||||
diskset: diskset(
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/osdisk",
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk1",
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk2",
|
|
||||||
"/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/datadisk3"),
|
|
||||||
wantSnapshotset: diskset(
|
|
||||||
"/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/osdisk-snap",
|
|
||||||
"/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datadisk-snap0",
|
|
||||||
"/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datadisk-snap1",
|
|
||||||
"/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datadisk-snap2",
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid ResourceID",
|
|
||||||
fields: fields{
|
|
||||||
OSDiskSnapshotID: "notaresourceid",
|
|
||||||
Location: "region1",
|
|
||||||
},
|
|
||||||
diskset: diskset("/subscriptions/12345/resourceGroups/group1/providers/Microsoft.Compute/disks/disk1"),
|
|
||||||
want: multistep.ActionHalt,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
expectedPutBody := regexp.MustCompile(`[\s\n]`).ReplaceAllString(tt.expectedPutBody, "")
|
|
||||||
|
|
||||||
m := compute.NewSnapshotsClient("subscriptionId")
|
|
||||||
m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
|
||||||
if r.Method != "PUT" {
|
|
||||||
t.Fatal("Expected only a PUT call")
|
|
||||||
}
|
|
||||||
if expectedPutBody != "" {
|
|
||||||
b, _ := ioutil.ReadAll(r.Body)
|
|
||||||
if string(b) != expectedPutBody {
|
|
||||||
t.Errorf("expected body to be %v, but got %v", expectedPutBody, string(b))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &http.Response{
|
|
||||||
Request: r,
|
|
||||||
StatusCode: 200,
|
|
||||||
}, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("azureclient", &client.AzureClientSetMock{
|
|
||||||
SnapshotsClientMock: m,
|
|
||||||
})
|
|
||||||
state.Put("ui", packersdk.TestUi(t))
|
|
||||||
state.Put(stateBagKey_Diskset, tt.diskset)
|
|
||||||
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
s := &StepCreateSnapshotset{
|
|
||||||
OSDiskSnapshotID: tt.fields.OSDiskSnapshotID,
|
|
||||||
DataDiskSnapshotIDPrefix: tt.fields.DataDiskSnapshotIDPrefix,
|
|
||||||
Location: tt.fields.Location,
|
|
||||||
}
|
|
||||||
if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("StepCreateSnapshot.Run() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(tt.wantSnapshotset) > 0 {
|
|
||||||
got := state.Get(stateBagKey_Snapshotset).(Diskset)
|
|
||||||
if !reflect.DeepEqual(got, tt.wantSnapshotset) {
|
|
||||||
t.Errorf("Snapshotset = %v, want %v", got, tt.wantSnapshotset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateSnapshot_Cleanup_skipped(t *testing.T) {
|
|
||||||
m := compute.NewSnapshotsClient("subscriptionId")
|
|
||||||
m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
|
||||||
t.Fatalf("clean up should be skipped, did not expect HTTP calls")
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("azureclient", &client.AzureClientSetMock{
|
|
||||||
SnapshotsClientMock: m,
|
|
||||||
})
|
|
||||||
state.Put("ui", packersdk.TestUi(t))
|
|
||||||
|
|
||||||
s := &StepCreateSnapshotset{
|
|
||||||
SkipCleanup: true,
|
|
||||||
}
|
|
||||||
s.Cleanup(state)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateSnapshot_Cleanup(t *testing.T) {
|
|
||||||
m := compute.NewSnapshotsClient("subscriptionId")
|
|
||||||
{
|
|
||||||
expectedCalls := []string{
|
|
||||||
"POST /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/ossnap/endGetAccess",
|
|
||||||
"DELETE /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/ossnap",
|
|
||||||
"POST /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap1/endGetAccess",
|
|
||||||
"DELETE /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap1",
|
|
||||||
"POST /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap2/endGetAccess",
|
|
||||||
"DELETE /subscriptions/subscriptionId/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap2",
|
|
||||||
}
|
|
||||||
|
|
||||||
m.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
|
||||||
got := r.Method + " " + r.URL.Path
|
|
||||||
found := false
|
|
||||||
for i, call := range expectedCalls {
|
|
||||||
if call == got {
|
|
||||||
// swap i with last and drop last
|
|
||||||
expectedCalls[i] = expectedCalls[len(expectedCalls)-1]
|
|
||||||
expectedCalls = expectedCalls[:len(expectedCalls)-1]
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
t.Errorf("unexpected HTTP call: %v, wanted one of %q", got, expectedCalls)
|
|
||||||
return &http.Response{
|
|
||||||
Request: r,
|
|
||||||
StatusCode: 599, // 500 is retried
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return &http.Response{
|
|
||||||
Request: r,
|
|
||||||
StatusCode: 200,
|
|
||||||
}, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("azureclient", &client.AzureClientSetMock{
|
|
||||||
SnapshotsClientMock: m,
|
|
||||||
})
|
|
||||||
state.Put("ui", packersdk.TestUi(t))
|
|
||||||
|
|
||||||
s := &StepCreateSnapshotset{
|
|
||||||
SkipCleanup: false,
|
|
||||||
snapshots: diskset(
|
|
||||||
"/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/ossnap",
|
|
||||||
"/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap1",
|
|
||||||
"/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Compute/snapshots/datasnap2"),
|
|
||||||
}
|
|
||||||
s.Cleanup(state)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateSnapshotset_Cleanup(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
OSDiskSnapshotID string
|
|
||||||
DataDiskSnapshotIDPrefix string
|
|
||||||
Location string
|
|
||||||
SkipCleanup bool
|
|
||||||
snapshots Diskset
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
state multistep.StateBag
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
s := &StepCreateSnapshotset{
|
|
||||||
OSDiskSnapshotID: tt.fields.OSDiskSnapshotID,
|
|
||||||
DataDiskSnapshotIDPrefix: tt.fields.DataDiskSnapshotIDPrefix,
|
|
||||||
Location: tt.fields.Location,
|
|
||||||
SkipCleanup: tt.fields.SkipCleanup,
|
|
||||||
snapshots: tt.fields.snapshots,
|
|
||||||
}
|
|
||||||
s.Cleanup(tt.args.state)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue