Add to vagrant post-processor support for Azure

This commit is contained in:
Patrick Double 2018-08-08 10:04:28 -05:00
parent 0633189c98
commit d796edc783
8 changed files with 213 additions and 14 deletions

View File

@ -18,6 +18,9 @@ type AdditionalDiskArtifact struct {
} }
type Artifact struct { type Artifact struct {
// OS type: Linux, Windows
OSType string
// VHD // VHD
StorageAccountLocation string StorageAccountLocation string
OSDiskUri string OSDiskUri string
@ -29,20 +32,23 @@ type Artifact struct {
ManagedImageResourceGroupName string ManagedImageResourceGroupName string
ManagedImageName string ManagedImageName string
ManagedImageLocation string ManagedImageLocation string
ManagedImageId string
// Additional Disks // Additional Disks
AdditionalDisks *[]AdditionalDiskArtifact AdditionalDisks *[]AdditionalDiskArtifact
} }
func NewManagedImageArtifact(resourceGroup, name, location string) (*Artifact, error) { func NewManagedImageArtifact(osType, resourceGroup, name, location, id string) (*Artifact, error) {
return &Artifact{ return &Artifact{
ManagedImageResourceGroupName: resourceGroup, ManagedImageResourceGroupName: resourceGroup,
ManagedImageName: name, ManagedImageName: name,
ManagedImageLocation: location, ManagedImageLocation: location,
ManagedImageId: id,
OSType: osType,
}, nil }, nil
} }
func NewArtifact(template *CaptureTemplate, getSasUrl func(name string) string) (*Artifact, error) { func NewArtifact(template *CaptureTemplate, getSasUrl func(name string) string, osType string) (*Artifact, error) {
if template == nil { if template == nil {
return nil, fmt.Errorf("nil capture template") return nil, fmt.Errorf("nil capture template")
} }
@ -76,6 +82,7 @@ func NewArtifact(template *CaptureTemplate, getSasUrl func(name string) string)
} }
return &Artifact{ return &Artifact{
OSType: osType,
OSDiskUri: vhdUri.String(), OSDiskUri: vhdUri.String(),
OSDiskUriReadOnlySas: getSasUrl(getStorageUrlPath(vhdUri)), OSDiskUriReadOnlySas: getSasUrl(getStorageUrlPath(vhdUri)),
TemplateUri: templateUri.String(), TemplateUri: templateUri.String(),
@ -142,9 +149,11 @@ func (a *Artifact) String() string {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("%s:\n\n", a.BuilderId())) buf.WriteString(fmt.Sprintf("%s:\n\n", a.BuilderId()))
buf.WriteString(fmt.Sprintf("OSType: %s\n", a.OSType))
if a.isManagedImage() { if a.isManagedImage() {
buf.WriteString(fmt.Sprintf("ManagedImageResourceGroupName: %s\n", a.ManagedImageResourceGroupName)) buf.WriteString(fmt.Sprintf("ManagedImageResourceGroupName: %s\n", a.ManagedImageResourceGroupName))
buf.WriteString(fmt.Sprintf("ManagedImageName: %s\n", a.ManagedImageName)) 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)) buf.WriteString(fmt.Sprintf("ManagedImageLocation: %s\n", a.ManagedImageLocation))
} else { } else {
buf.WriteString(fmt.Sprintf("StorageAccountLocation: %s\n", a.StorageAccountLocation)) buf.WriteString(fmt.Sprintf("StorageAccountLocation: %s\n", a.StorageAccountLocation))

View File

@ -28,7 +28,7 @@ func TestArtifactId(t *testing.T) {
}, },
} }
artifact, err := NewArtifact(&template, getFakeSasUrl) artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux")
if err != nil { if err != nil {
t.Fatalf("err=%s", err) t.Fatalf("err=%s", err)
} }
@ -59,7 +59,7 @@ func TestArtifactString(t *testing.T) {
}, },
} }
artifact, err := NewArtifact(&template, getFakeSasUrl) artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux")
if err != nil { if err != nil {
t.Fatalf("err=%s", err) t.Fatalf("err=%s", err)
} }
@ -80,6 +80,9 @@ func TestArtifactString(t *testing.T) {
if !strings.Contains(testSubject, "StorageAccountLocation: southcentralus") { if !strings.Contains(testSubject, "StorageAccountLocation: southcentralus") {
t.Errorf("Expected String() output to contain StorageAccountLocation") 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) { func TestAdditionalDiskArtifactString(t *testing.T) {
@ -107,7 +110,7 @@ func TestAdditionalDiskArtifactString(t *testing.T) {
}, },
} }
artifact, err := NewArtifact(&template, getFakeSasUrl) artifact, err := NewArtifact(&template, getFakeSasUrl, "Linux")
if err != nil { if err != nil {
t.Fatalf("err=%s", err) t.Fatalf("err=%s", err)
} }
@ -128,6 +131,9 @@ func TestAdditionalDiskArtifactString(t *testing.T) {
if !strings.Contains(testSubject, "StorageAccountLocation: southcentralus") { if !strings.Contains(testSubject, "StorageAccountLocation: southcentralus") {
t.Errorf("Expected String() output to contain StorageAccountLocation") 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") { 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") t.Errorf("Expected String() output to contain AdditionalDiskUri")
} }
@ -154,7 +160,7 @@ func TestArtifactProperties(t *testing.T) {
}, },
} }
testSubject, err := NewArtifact(&template, getFakeSasUrl) testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux")
if err != nil { if err != nil {
t.Fatalf("err=%s", err) t.Fatalf("err=%s", err)
} }
@ -174,6 +180,9 @@ func TestArtifactProperties(t *testing.T) {
if testSubject.StorageAccountLocation != "southcentralus" { if testSubject.StorageAccountLocation != "southcentralus" {
t.Errorf("Expected StorageAccountLocation to be 'southcentral', but got %s", testSubject.StorageAccountLocation) 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) { func TestAdditionalDiskArtifactProperties(t *testing.T) {
@ -201,7 +210,7 @@ func TestAdditionalDiskArtifactProperties(t *testing.T) {
}, },
} }
testSubject, err := NewArtifact(&template, getFakeSasUrl) testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux")
if err != nil { if err != nil {
t.Fatalf("err=%s", err) t.Fatalf("err=%s", err)
} }
@ -221,6 +230,9 @@ func TestAdditionalDiskArtifactProperties(t *testing.T) {
if testSubject.StorageAccountLocation != "southcentralus" { if testSubject.StorageAccountLocation != "southcentralus" {
t.Errorf("Expected StorageAccountLocation to be 'southcentral', but got %s", testSubject.StorageAccountLocation) 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 { if testSubject.AdditionalDisks == nil {
t.Errorf("Expected AdditionalDisks to be not nil") t.Errorf("Expected AdditionalDisks to be not nil")
} }
@ -253,7 +265,7 @@ func TestArtifactOverHyphenatedCaptureUri(t *testing.T) {
}, },
} }
testSubject, err := NewArtifact(&template, getFakeSasUrl) testSubject, err := NewArtifact(&template, getFakeSasUrl, "Linux")
if err != nil { if err != nil {
t.Fatalf("err=%s", err) t.Fatalf("err=%s", err)
} }
@ -266,7 +278,7 @@ func TestArtifactOverHyphenatedCaptureUri(t *testing.T) {
func TestArtifactRejectMalformedTemplates(t *testing.T) { func TestArtifactRejectMalformedTemplates(t *testing.T) {
template := CaptureTemplate{} template := CaptureTemplate{}
_, err := NewArtifact(&template, getFakeSasUrl) _, err := NewArtifact(&template, getFakeSasUrl, "Linux")
if err == nil { if err == nil {
t.Fatalf("Expected artifact creation to fail, but it succeeded.") t.Fatalf("Expected artifact creation to fail, but it succeeded.")
} }
@ -289,7 +301,7 @@ func TestArtifactRejectMalformedStorageUri(t *testing.T) {
}, },
} }
_, err := NewArtifact(&template, getFakeSasUrl) _, err := NewArtifact(&template, getFakeSasUrl, "Linux")
if err == nil { if err == nil {
t.Fatalf("Expected artifact creation to fail, but it succeeded.") t.Fatalf("Expected artifact creation to fail, but it succeeded.")
} }

View File

@ -254,8 +254,14 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return nil, errors.New("Build was halted.") return nil, errors.New("Build was halted.")
} }
osType := "Linux"
if b.config.OSType == constants.Target_Windows {
osType = "Windows"
}
if b.config.isManagedImage() { if b.config.isManagedImage() {
return NewManagedImageArtifact(b.config.ManagedImageResourceGroupName, b.config.ManagedImageName, b.config.manageImageLocation) managedImageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", b.config.SubscriptionID, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName)
return NewManagedImageArtifact(osType, b.config.ManagedImageResourceGroupName, b.config.ManagedImageName, b.config.manageImageLocation, managedImageID)
} else if template, ok := b.stateBag.GetOk(constants.ArmCaptureTemplate); ok { } else if template, ok := b.stateBag.GetOk(constants.ArmCaptureTemplate); ok {
return NewArtifact( return NewArtifact(
template.(*CaptureTemplate), template.(*CaptureTemplate),
@ -266,7 +272,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
options.Expiry = time.Now().AddDate(0, 1, 0).UTC() // one month options.Expiry = time.Now().AddDate(0, 1, 0).UTC() // one month
sasUrl, _ := blob.GetSASURI(options) sasUrl, _ := blob.GetSASURI(options)
return sasUrl return sasUrl
}) },
osType)
} }
return &Artifact{}, nil return &Artifact{}, nil

View File

@ -7,6 +7,7 @@ type MockArtifact struct {
IdValue string IdValue string
StateValues map[string]interface{} StateValues map[string]interface{}
DestroyCalled bool DestroyCalled bool
StringValue string
} }
func (a *MockArtifact) BuilderId() string { func (a *MockArtifact) BuilderId() string {
@ -34,8 +35,12 @@ func (a *MockArtifact) Id() string {
return id return id
} }
func (*MockArtifact) String() string { func (a *MockArtifact) String() string {
return "string" str := a.StringValue
if str == "" {
str = "string"
}
return str
} }
func (a *MockArtifact) State(name string) interface{} { func (a *MockArtifact) State(name string) interface{} {

View File

@ -0,0 +1,67 @@
package vagrant
import (
"fmt"
"strings"
"github.com/hashicorp/packer/packer"
)
type AzureProvider struct{}
func (p *AzureProvider) KeepInputArtifact() bool {
return true
}
func (p *AzureProvider) Process(ui packer.Ui, artifact packer.Artifact, dir string) (vagrantfile string, metadata map[string]interface{}, err error) {
// Create the metadata
metadata = map[string]interface{}{"provider": "azure"}
var AzureImageProps map[string]string
AzureImageProps = make(map[string]string)
// HACK(double16): It appears we can not access the Azure Artifact directly, so parse String()
artifactString := artifact.String()
ui.Message(fmt.Sprintf("artifact string: '%s'", artifactString))
lines := strings.Split(artifactString, "\n")
for l := 0; l < len(lines); l++ {
split := strings.Split(lines[l], ": ")
if len(split) > 1 {
AzureImageProps[strings.TrimSpace(split[0])] = strings.TrimSpace(split[1])
}
}
ui.Message(fmt.Sprintf("artifact string parsed: %+v", AzureImageProps))
if AzureImageProps["ManagedImageId"] != "" {
vagrantfile = fmt.Sprintf(managedImageVagrantfile, AzureImageProps["ManagedImageLocation"], AzureImageProps["ManagedImageId"])
} else if AzureImageProps["OSDiskUri"] != "" {
vagrantfile = fmt.Sprintf(vhdVagrantfile, AzureImageProps["StorageAccountLocation"], AzureImageProps["OSDiskUri"], AzureImageProps["OSType"])
} else {
err = fmt.Errorf("No managed image nor VHD URI found in artifact: %s", artifactString)
return
}
return
}
var managedImageVagrantfile = `
Vagrant.configure("2") do |config|
config.vm.provider :azure do |azure, override|
azure.location = "%s"
azure.vm_managed_image_id = "%s"
override.winrm.transport = :ssl
override.winrm.port = 5986
end
end
`
var vhdVagrantfile = `
Vagrant.configure("2") do |config|
config.vm.provider :azure do |azure, override|
azure.location = "%s"
azure.vm_vhd_uri = "%s"
azure.vm_operating_system = "%s"
override.winrm.transport = :ssl
override.winrm.port = 5986
end
end
`

View File

@ -0,0 +1,94 @@
package vagrant
import (
"strings"
"testing"
"github.com/hashicorp/packer/packer"
)
func TestAzureProvider_impl(t *testing.T) {
var _ Provider = new(AzureProvider)
}
func TestAzureProvider_KeepInputArtifact(t *testing.T) {
p := new(AzureProvider)
if !p.KeepInputArtifact() {
t.Fatal("should keep input artifact")
}
}
func TestAzureProvider_ManagedImage(t *testing.T) {
p := new(AzureProvider)
ui := testUi()
artifact := &packer.MockArtifact{
StringValue: `Azure.ResourceManagement.VMImage:
OSType: Linux
ManagedImageResourceGroupName: packerruns
ManagedImageName: packer-1533651633
ManagedImageId: /subscriptions/e6229913-d9c3-4ddd-99a4-9e1ef3beaa1b/resourceGroups/packerruns/providers/Microsoft.Compute/images/packer-1533675589
ManagedImageLocation: westus`,
}
vagrantfile, _, err := p.Process(ui, artifact, "foo")
if err != nil {
t.Fatalf("should not have error: %s", err)
}
result := `azure.location = "westus"`
if !strings.Contains(vagrantfile, result) {
t.Fatalf("wrong substitution: %s", vagrantfile)
}
result = `azure.vm_managed_image_id = "/subscriptions/e6229913-d9c3-4ddd-99a4-9e1ef3beaa1b/resourceGroups/packerruns/providers/Microsoft.Compute/images/packer-1533675589"`
if !strings.Contains(vagrantfile, result) {
t.Fatalf("wrong substitution: %s", vagrantfile)
}
// DO NOT set resource group in Vagrantfile, it should be separate from the image
result = `azure.resource_group_name`
if strings.Contains(vagrantfile, result) {
t.Fatalf("wrong substitution: %s", vagrantfile)
}
result = `azure.vm_operating_system`
if strings.Contains(vagrantfile, result) {
t.Fatalf("wrong substitution: %s", vagrantfile)
}
}
func TestAzureProvider_VHD(t *testing.T) {
p := new(AzureProvider)
ui := testUi()
artifact := &packer.MockArtifact{
IdValue: "https://packerbuildswest.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.96ed2120-591d-4900-95b0-ee8e985f2213.vhd",
StringValue: `Azure.ResourceManagement.VMImage:
OSType: Linux
StorageAccountLocation: westus
OSDiskUri: https://packerbuildswest.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.96ed2120-591d-4900-95b0-ee8e985f2213.vhd
OSDiskUriReadOnlySas: https://packerbuildswest.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.96ed2120-591d-4900-95b0-ee8e985f2213.vhd?se=2018-09-07T18%3A36%3A34Z&sig=xUiFvwAviPYoP%2Bc91vErqvwYR1eK4x%2BAx7YLMe84zzU%3D&sp=r&sr=b&sv=2016-05-31
TemplateUri: https://packerbuildswest.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.96ed2120-591d-4900-95b0-ee8e985f2213.json
TemplateUriReadOnlySas: https://packerbuildswest.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-vmTemplate.96ed2120-591d-4900-95b0-ee8e985f2213.json?se=2018-09-07T18%3A36%3A34Z&sig=lDxePyAUCZbfkB5ddiofimXfwk5INn%2F9E2BsnqIKC9Q%3D&sp=r&sr=b&sv=2016-05-31`,
}
vagrantfile, _, err := p.Process(ui, artifact, "foo")
if err != nil {
t.Fatalf("should not have error: %s", err)
}
result := `azure.location = "westus"`
if !strings.Contains(vagrantfile, result) {
t.Fatalf("wrong substitution: %s", vagrantfile)
}
result = `azure.vm_vhd_uri = "https://packerbuildswest.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.96ed2120-591d-4900-95b0-ee8e985f2213.vhd"`
if !strings.Contains(vagrantfile, result) {
t.Fatalf("wrong substitution: %s", vagrantfile)
}
result = `azure.vm_operating_system = "Linux"`
if !strings.Contains(vagrantfile, result) {
t.Fatalf("wrong substitution: %s", vagrantfile)
}
// DO NOT set resource group in Vagrantfile, it should be separate from the image
result = `azure.resource_group_name`
if strings.Contains(vagrantfile, result) {
t.Fatalf("wrong substitution: %s", vagrantfile)
}
}

View File

@ -31,6 +31,7 @@ var builtins = map[string]string{
"MSOpenTech.hyperv": "hyperv", "MSOpenTech.hyperv": "hyperv",
"transcend.qemu": "libvirt", "transcend.qemu": "libvirt",
"ustream.lxc": "lxc", "ustream.lxc": "lxc",
"Azure.ResourceManagement.VMImage": "azure",
"packer.post-processor.docker-import": "docker", "packer.post-processor.docker-import": "docker",
"packer.post-processor.docker-tag": "docker", "packer.post-processor.docker-tag": "docker",
"packer.post-processor.docker-push": "docker", "packer.post-processor.docker-push": "docker",
@ -244,6 +245,8 @@ func providerForName(name string) Provider {
return new(GoogleProvider) return new(GoogleProvider)
case "lxc": case "lxc":
return new(LXCProvider) return new(LXCProvider)
case "azure":
return new(AzureProvider)
case "docker": case "docker":
return new(DockerProvider) return new(DockerProvider)
default: default:

View File

@ -33,6 +33,7 @@ providers.
- AWS - AWS
- DigitalOcean - DigitalOcean
- Google - Google
- Azure
- Hyper-V - Hyper-V
- LXC - LXC
- Parallels - Parallels
@ -108,6 +109,7 @@ The available provider names are:
- `aws` - `aws`
- `digitalocean` - `digitalocean`
- `google` - `google`
- `azure`
- `hyperv` - `hyperv`
- `parallels` - `parallels`
- `libvirt` - `libvirt`