packer-cn/builder/azure/chroot/builder.go

458 lines
16 KiB
Go
Raw Normal View History

2019-09-26 14:44:35 -04:00
//go:generate struct-markdown
build using HCL2 (#8423) This follows #8232 which added the code to generate the code required to parse HCL files for each packer component. All old config files of packer will keep on working the same. Packer takes one argument. When a directory is passed, all files in the folder with a name ending with “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format. When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed using the HCL2 format. For every other case; the old packer style will be used. ## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields ## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file. This is a breaking change for packer plugins. a packer component can be a: builder/provisioner/post-processor each component interface now gets a `ConfigSpec() hcldec.ObjectSpec` which allows packer to tell what is the layout of the hcl2 config meant to configure that specific component. This ObjectSpec is sent through the wire (RPC) and a cty.Value is now sent through the already existing configuration entrypoints: Provisioner.Prepare(raws ...interface{}) error Builder.Prepare(raws ...interface{}) ([]string, error) PostProcessor.Configure(raws ...interface{}) error close #1768 Example hcl files: ```hcl // file amazon-ebs-kms-key/run.pkr.hcl build { sources = [ "source.amazon-ebs.first", ] provisioner "shell" { inline = [ "sleep 5" ] } post-processor "shell-local" { inline = [ "sleep 5" ] } } // amazon-ebs-kms-key/source.pkr.hcl source "amazon-ebs" "first" { ami_name = "hcl2-test" region = "us-east-1" instance_type = "t2.micro" kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c" encrypt_boot = true source_ami_filter { filters { virtualization-type = "hvm" name = "amzn-ami-hvm-????.??.?.????????-x86_64-gp2" root-device-type = "ebs" } most_recent = true owners = ["amazon"] } launch_block_device_mappings { device_name = "/dev/xvda" volume_size = 20 volume_type = "gp2" delete_on_termination = "true" } launch_block_device_mappings { device_name = "/dev/xvdf" volume_size = 500 volume_type = "gp2" delete_on_termination = true encrypted = true } ami_regions = ["eu-central-1"] run_tags { Name = "packer-solr-something" stack-name = "DevOps Tools" } communicator = "ssh" ssh_pty = true ssh_username = "ec2-user" associate_public_ip_address = true } ```
2019-12-17 05:25:56 -05:00
//go:generate mapstructure-to-hcl2 -type Config
2019-09-26 14:44:35 -04:00
2019-10-11 11:32:02 -04:00
// 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
2019-09-26 14:44:35 -04:00
// mounting the root disk and chrooting into that directory.
// It then creates a managed image from that attached disk.
2019-05-16 20:24:53 -04:00
package chroot
import (
"context"
"errors"
2019-05-27 02:20:11 -04:00
"fmt"
"log"
2019-05-16 20:24:53 -04:00
"runtime"
2019-06-03 01:27:33 -04:00
"strings"
2019-05-16 20:24:53 -04:00
build using HCL2 (#8423) This follows #8232 which added the code to generate the code required to parse HCL files for each packer component. All old config files of packer will keep on working the same. Packer takes one argument. When a directory is passed, all files in the folder with a name ending with “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format. When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed using the HCL2 format. For every other case; the old packer style will be used. ## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields ## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file. This is a breaking change for packer plugins. a packer component can be a: builder/provisioner/post-processor each component interface now gets a `ConfigSpec() hcldec.ObjectSpec` which allows packer to tell what is the layout of the hcl2 config meant to configure that specific component. This ObjectSpec is sent through the wire (RPC) and a cty.Value is now sent through the already existing configuration entrypoints: Provisioner.Prepare(raws ...interface{}) error Builder.Prepare(raws ...interface{}) ([]string, error) PostProcessor.Configure(raws ...interface{}) error close #1768 Example hcl files: ```hcl // file amazon-ebs-kms-key/run.pkr.hcl build { sources = [ "source.amazon-ebs.first", ] provisioner "shell" { inline = [ "sleep 5" ] } post-processor "shell-local" { inline = [ "sleep 5" ] } } // amazon-ebs-kms-key/source.pkr.hcl source "amazon-ebs" "first" { ami_name = "hcl2-test" region = "us-east-1" instance_type = "t2.micro" kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c" encrypt_boot = true source_ami_filter { filters { virtualization-type = "hvm" name = "amzn-ami-hvm-????.??.?.????????-x86_64-gp2" root-device-type = "ebs" } most_recent = true owners = ["amazon"] } launch_block_device_mappings { device_name = "/dev/xvda" volume_size = 20 volume_type = "gp2" delete_on_termination = "true" } launch_block_device_mappings { device_name = "/dev/xvdf" volume_size = 500 volume_type = "gp2" delete_on_termination = true encrypted = true } ami_regions = ["eu-central-1"] run_tags { Name = "packer-solr-something" stack-name = "DevOps Tools" } communicator = "ssh" ssh_pty = true ssh_username = "ec2-user" associate_public_ip_address = true } ```
2019-12-17 05:25:56 -05:00
"github.com/hashicorp/hcl/v2/hcldec"
2019-05-16 20:24:53 -04:00
azcommon "github.com/hashicorp/packer/builder/azure/common"
2019-05-27 02:20:11 -04:00
"github.com/hashicorp/packer/builder/azure/common/client"
2019-05-16 20:24:53 -04:00
"github.com/hashicorp/packer/common"
2019-10-24 00:21:08 -04:00
"github.com/hashicorp/packer/common/chroot"
2019-05-16 20:24:53 -04:00
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
2019-05-31 16:02:25 -04:00
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
2019-09-10 08:48:55 -04:00
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to"
2019-05-16 20:24:53 -04:00
)
2019-10-07 15:20:08 -04:00
// BuilderId is the unique ID for this builder
const BuilderId = "azure.chroot"
2019-09-26 14:44:35 -04:00
// Config is the configuration that is chained through the steps and settable
// from the template.
2019-05-16 20:24:53 -04:00
type Config struct {
common.PackerConfig `mapstructure:",squash"`
2019-05-31 18:15:56 -04:00
ClientConfig client.Config `mapstructure:",squash"`
2019-09-26 14:44:35 -04:00
// When set to `true`, starts with an empty, unpartitioned disk. Defaults to `false`.
2019-09-26 18:17:07 -04:00
FromScratch bool `mapstructure:"from_scratch"`
// Either a managed disk resource ID or a publisher:offer:sku:version specifier for plaform image sources.
2019-09-26 18:17:07 -04:00
Source string `mapstructure:"source" required:"true"`
sourceType sourceType
2019-05-27 02:20:11 -04:00
2019-09-26 14:44:35 -04:00
// 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}}`.
2019-09-26 18:17:07 -04:00
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
2019-09-26 14:44:35 -04:00
// and filesystem creation commands. The path to the device is provided by `{{.Device}}`.
2019-09-26 18:17:07 -04:00
PreMountCommands []string `mapstructure:"pre_mount_commands"`
// Options to supply the `mount` command when mounting devices. Each option will be prefixed with
2019-09-26 14:44:35 -04:00
// `-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.
2019-09-26 18:17:07 -04:00
MountOptions []string `mapstructure:"mount_options"`
2019-09-26 14:44:35 -04:00
// The partition number containing the / partition. By default this is the first partition of the volume.
2019-09-26 18:17:07 -04:00
MountPartition string `mapstructure:"mount_partition"`
2019-09-26 14:44:35 -04:00
// The path where the volume will be mounted. This is where the chroot environment will be. This defaults
2019-09-26 18:17:07 -04:00
// to `/mnt/packer-amazon-chroot-volumes/{{.Device}}`. This is a configuration template where the `.Device`
2019-09-26 14:44:35 -04:00
// variable is replaced with the name of the device where the volume is attached.
2019-09-26 18:17:07 -04:00
MountPath string `mapstructure:"mount_path"`
2019-09-26 14:44:35 -04:00
// 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}}`.
2019-09-26 18:17:07 -04:00
PostMountCommands []string `mapstructure:"post_mount_commands"`
// This is a list of devices to mount into the chroot environment. This configuration parameter requires
2019-09-26 14:44:35 -04:00
// some additional documentation which is in the "Chroot Mounts" section below. Please read that section
// for more information on how to use this.
2019-09-26 18:17:07 -04:00
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
// Paths to files on the running Azure instance that will be copied into the chroot environment prior to
2019-09-26 14:44:35 -04:00
// 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.
2019-09-26 18:17:07 -04:00
CopyFiles []string `mapstructure:"copy_files"`
2019-09-26 14:44:35 -04:00
// The name of the temporary disk that will be created in the resource group of the VM that Packer is
// running on. Will be generated if not set.
2019-09-26 18:17:07 -04:00
TemporaryOSDiskName string `mapstructure:"temporary_os_disk_name"`
2019-09-26 14:44:35 -04:00
// 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`
2019-09-26 18:17:07 -04:00
OSDiskSizeGB int32 `mapstructure:"os_disk_size_gb"`
2019-09-26 14:44:35 -04:00
// 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`.
2019-05-31 16:02:25 -04:00
OSDiskStorageAccountType string `mapstructure:"os_disk_storage_account_type"`
2019-09-26 14:44:35 -04:00
// 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`
2019-09-26 18:17:07 -04:00
OSDiskCacheType string `mapstructure:"os_disk_cache_type"`
2019-09-26 14:44:35 -04:00
// If set to `true`, leaves the temporary disk behind in the Packer VM resource group. Defaults to `false`
2019-09-26 18:17:07 -04:00
OSDiskSkipCleanup bool `mapstructure:"os_disk_skip_cleanup"`
2019-05-31 16:02:25 -04:00
2019-09-26 14:44:35 -04:00
// The image to create using this build.
2019-09-26 18:17:07 -04:00
ImageResourceID string `mapstructure:"image_resource_id" required:"true"`
2019-09-26 14:44:35 -04:00
// The [Hyper-V generation type](https://docs.microsoft.com/en-us/rest/api/compute/images/createorupdate#hypervgenerationtypes).
2019-10-03 00:45:39 -04:00
// Defaults to `V1`.
2019-06-03 01:27:33 -04:00
ImageHyperVGeneration string `mapstructure:"image_hyperv_generation"`
2019-05-27 02:20:11 -04:00
2019-05-16 20:24:53 -04:00
ctx interpolate.Context
}
2019-09-10 08:48:55 -04:00
type sourceType string
const (
sourcePlatformImage sourceType = "PlatformImage"
sourceDisk sourceType = "Disk"
)
2019-09-26 18:17:07 -04:00
// GetContext implements ContextProvider to allow steps to use the config context
2019-09-26 14:44:35 -04:00
// for template interpolation
2019-06-03 01:27:33 -04:00
func (c *Config) GetContext() interpolate.Context {
return c.ctx
}
2019-05-16 20:24:53 -04:00
type Builder struct {
config Config
runner multistep.Runner
}
build using HCL2 (#8423) This follows #8232 which added the code to generate the code required to parse HCL files for each packer component. All old config files of packer will keep on working the same. Packer takes one argument. When a directory is passed, all files in the folder with a name ending with “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format. When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed using the HCL2 format. For every other case; the old packer style will be used. ## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields ## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file. This is a breaking change for packer plugins. a packer component can be a: builder/provisioner/post-processor each component interface now gets a `ConfigSpec() hcldec.ObjectSpec` which allows packer to tell what is the layout of the hcl2 config meant to configure that specific component. This ObjectSpec is sent through the wire (RPC) and a cty.Value is now sent through the already existing configuration entrypoints: Provisioner.Prepare(raws ...interface{}) error Builder.Prepare(raws ...interface{}) ([]string, error) PostProcessor.Configure(raws ...interface{}) error close #1768 Example hcl files: ```hcl // file amazon-ebs-kms-key/run.pkr.hcl build { sources = [ "source.amazon-ebs.first", ] provisioner "shell" { inline = [ "sleep 5" ] } post-processor "shell-local" { inline = [ "sleep 5" ] } } // amazon-ebs-kms-key/source.pkr.hcl source "amazon-ebs" "first" { ami_name = "hcl2-test" region = "us-east-1" instance_type = "t2.micro" kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c" encrypt_boot = true source_ami_filter { filters { virtualization-type = "hvm" name = "amzn-ami-hvm-????.??.?.????????-x86_64-gp2" root-device-type = "ebs" } most_recent = true owners = ["amazon"] } launch_block_device_mappings { device_name = "/dev/xvda" volume_size = 20 volume_type = "gp2" delete_on_termination = "true" } launch_block_device_mappings { device_name = "/dev/xvdf" volume_size = 500 volume_type = "gp2" delete_on_termination = true encrypted = true } ami_regions = ["eu-central-1"] run_tags { Name = "packer-solr-something" stack-name = "DevOps Tools" } communicator = "ssh" ssh_pty = true ssh_username = "ec2-user" associate_public_ip_address = true } ```
2019-12-17 05:25:56 -05:00
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
2019-05-16 20:24:53 -04:00
b.config.ctx.Funcs = azcommon.TemplateFuncs
2019-10-04 14:59:11 -04:00
b.config.ctx.Funcs["vm"] = CreateVMMetadataTemplateFunc()
2019-05-16 20:24:53 -04:00
err := config.Decode(&b.config, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
2019-05-31 14:49:35 -04:00
// these fields are interpolated in the steps,
// when more information is available
"command_wrapper",
"post_mount_commands",
"pre_mount_commands",
"mount_path",
2019-05-16 20:24:53 -04:00
},
},
}, raws...)
2019-10-04 14:59:11 -04:00
if err != nil {
return nil, nil, err
2019-10-04 14:59:11 -04:00
}
2019-05-16 20:24:53 -04:00
2019-06-03 04:33:31 -04:00
var errs *packer.MultiError
var warns []string
2019-05-31 16:02:25 -04:00
// Defaults
2019-05-31 18:15:56 -04:00
err = b.config.ClientConfig.SetDefaultValues()
if err != nil {
return nil, nil, err
2019-05-31 18:15:56 -04:00
}
2019-05-31 16:02:25 -04:00
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 == "" {
2019-05-31 18:15:56 -04:00
b.config.MountPath = "/mnt/packer-azure-chroot-disks/{{.Device}}"
2019-05-31 16:02:25 -04:00
}
if b.config.MountPartition == "" {
b.config.MountPartition = "1"
}
2019-06-03 01:27:33 -04:00
if b.config.TemporaryOSDiskName == "" {
2019-06-03 04:33:31 -04:00
if def, err := interpolate.Render("PackerTemp-{{timestamp}}", &b.config.ctx); err == nil {
b.config.TemporaryOSDiskName = def
} else {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("unable to render temporary disk name: %s", err))
}
2019-06-03 01:27:33 -04:00
}
2019-05-27 02:20:11 -04:00
if b.config.OSDiskStorageAccountType == "" {
b.config.OSDiskStorageAccountType = string(compute.PremiumLRS)
}
2019-05-31 16:02:25 -04:00
if b.config.OSDiskCacheType == "" {
b.config.OSDiskCacheType = string(compute.CachingTypesReadOnly)
}
2019-06-03 01:27:33 -04:00
if b.config.ImageHyperVGeneration == "" {
2019-10-03 00:45:39 -04:00
b.config.ImageHyperVGeneration = string(compute.V1)
2019-05-31 18:15:56 -04:00
}
2019-05-16 20:24:53 -04:00
// checks, accumulate any errors or warnings
2019-05-27 02:20:11 -04:00
if b.config.FromScratch {
2019-06-03 04:33:31 -04:00
if b.config.Source != "" {
errs = packer.MultiErrorAppend(
errs, errors.New("source cannot be specified when building from_scratch"))
}
2019-05-27 02:20:11 -04:00
if b.config.OSDiskSizeGB == 0 {
errs = packer.MultiErrorAppend(
2019-05-31 18:15:56 -04:00
errs, errors.New("os_disk_size_gb is required with from_scratch"))
2019-05-27 02:20:11 -04:00
}
if len(b.config.PreMountCommands) == 0 {
errs = packer.MultiErrorAppend(
2019-05-31 14:49:35 -04:00
errs, errors.New("pre_mount_commands is required with from_scratch"))
}
2019-05-31 18:15:56 -04:00
} else {
2019-06-03 04:33:31 -04:00
if _, err := client.ParsePlatformImageURN(b.config.Source); err == nil {
log.Println("Source is platform image:", b.config.Source)
2019-09-10 08:48:55 -04:00
b.config.sourceType = sourcePlatformImage
} else if id, err := azure.ParseResourceID(b.config.Source); err == nil &&
strings.EqualFold(id.Provider, "Microsoft.Compute") && strings.EqualFold(id.ResourceType, "disks") {
log.Println("Source is a disk resource ID:", b.config.Source)
b.config.sourceType = sourceDisk
2019-06-03 04:33:31 -04:00
} else {
errs = packer.MultiErrorAppend(
2019-09-10 08:48:55 -04:00
errs, fmt.Errorf("source: %q is not a valid platform image specifier, nor is it a disk resource ID", b.config.Source))
2019-06-03 04:33:31 -04:00
}
2019-05-27 02:20:11 -04:00
}
2019-05-31 18:15:56 -04:00
if err := checkDiskCacheType(b.config.OSDiskCacheType); err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("os_disk_cache_type: %v", err))
2019-05-16 20:24:53 -04:00
}
2019-06-03 01:27:33 -04:00
2019-05-31 18:15:56 -04:00
if err := checkStorageAccountType(b.config.OSDiskStorageAccountType); err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("os_disk_storage_account_type: %v", err))
}
2019-06-03 01:27:33 -04:00
if b.config.ImageResourceID == "" {
errs = packer.MultiErrorAppend(errs, errors.New("image_resource_id is required"))
} else {
r, err := azure.ParseResourceID(b.config.ImageResourceID)
if err != nil ||
!strings.EqualFold(r.Provider, "Microsoft.Compute") ||
!strings.EqualFold(r.ResourceType, "images") {
errs = packer.MultiErrorAppend(fmt.Errorf(
"image_resource_id: %q is not a valid image resource id", b.config.ImageResourceID))
}
}
if err := checkHyperVGeneration(b.config.ImageHyperVGeneration); err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("image_hyperv_generation: %v", err))
}
2019-05-31 18:15:56 -04:00
if errs != nil {
return nil, warns, errs
2019-05-31 18:15:56 -04:00
}
packer.LogSecretFilter.Set(b.config.ClientConfig.ClientSecret, b.config.ClientConfig.ClientJWT)
return nil, warns, nil
2019-05-31 18:15:56 -04:00
}
func checkDiskCacheType(s string) interface{} {
for _, v := range compute.PossibleCachingTypesValues() {
if compute.CachingTypes(s) == v {
return nil
}
}
2019-06-03 01:27:33 -04:00
return fmt.Errorf("%q is not a valid value %v",
2019-05-31 18:15:56 -04:00
s, compute.PossibleCachingTypesValues())
}
func checkStorageAccountType(s string) interface{} {
2019-06-02 15:29:45 -04:00
for _, v := range compute.PossibleDiskStorageAccountTypesValues() {
if compute.DiskStorageAccountTypes(s) == v {
2019-05-31 18:15:56 -04:00
return nil
}
}
2019-06-03 01:27:33 -04:00
return fmt.Errorf("%q is not a valid value %v",
2019-06-02 15:29:45 -04:00
s, compute.PossibleDiskStorageAccountTypesValues())
2019-05-16 20:24:53 -04:00
}
2019-06-03 01:27:33 -04:00
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())
}
2019-05-16 20:24:53 -04:00
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
if runtime.GOOS != "linux" {
2019-05-31 14:49:35 -04:00
return nil, errors.New("the azure-chroot builder only works on Linux environments")
2019-05-16 20:24:53 -04:00
}
2019-05-31 18:15:56 -04:00
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)
}
2019-05-27 02:20:11 -04:00
wrappedCommand := func(command string) (string, error) {
ictx := b.config.ctx
ictx.Data = &struct{ Command string }{Command: command}
return interpolate.Render(b.config.CommandWrapper, &ictx)
}
2019-05-16 20:24:53 -04:00
// 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)
2019-05-31 16:02:25 -04:00
state.Put("azureclient", azcli)
state.Put("wrappedCommand", common.CommandWrapper(wrappedCommand))
2019-05-16 20:24:53 -04:00
2019-05-27 02:20:11 -04:00
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)
2019-05-16 20:24:53 -04:00
// Build the steps
var steps []multistep.Step
2019-06-03 04:33:31 -04:00
if b.config.FromScratch {
2019-05-27 02:20:11 -04:00
steps = append(steps,
&StepCreateNewDisk{
SubscriptionID: info.SubscriptionID,
ResourceGroup: info.ResourceGroupName,
2019-06-03 01:27:33 -04:00
DiskName: b.config.TemporaryOSDiskName,
2019-05-27 02:20:11 -04:00
DiskSizeGB: b.config.OSDiskSizeGB,
DiskStorageAccountType: b.config.OSDiskStorageAccountType,
2019-06-03 01:27:33 -04:00
HyperVGeneration: b.config.ImageHyperVGeneration,
Location: info.Location,
2019-05-27 02:20:11 -04:00
})
2019-06-03 04:33:31 -04:00
} else {
2019-09-10 08:48:55 -04:00
switch b.config.sourceType {
case sourcePlatformImage:
if pi, err := client.ParsePlatformImageURN(b.config.Source); err == nil {
if strings.EqualFold(pi.Version, "latest") {
vmi, err := azcli.VirtualMachineImagesClient().GetLatest(ctx, pi.Publisher, pi.Offer, pi.Sku, info.Location)
if err != nil {
return nil, fmt.Errorf("error retieving latest version of %q: %v", b.config.Source, err)
}
pi.Version = to.String(vmi.Name)
log.Println("Resolved latest version of source image:", pi.Version)
2019-06-03 04:33:31 -04:00
}
2019-09-10 08:48:55 -04:00
steps = append(steps,
2019-09-26 18:17:07 -04:00
2019-09-10 08:48:55 -04:00
&StepCreateNewDisk{
SubscriptionID: info.SubscriptionID,
ResourceGroup: info.ResourceGroupName,
DiskName: b.config.TemporaryOSDiskName,
DiskSizeGB: b.config.OSDiskSizeGB,
DiskStorageAccountType: b.config.OSDiskStorageAccountType,
HyperVGeneration: b.config.ImageHyperVGeneration,
Location: info.Location,
PlatformImage: pi,
SkipCleanup: b.config.OSDiskSkipCleanup,
})
} else {
panic("Unknown image source: " + b.config.Source)
2019-06-03 04:33:31 -04:00
}
2019-09-10 08:48:55 -04:00
case sourceDisk:
2019-06-03 04:33:31 -04:00
steps = append(steps,
2019-09-26 18:17:07 -04:00
&StepVerifySourceDisk{
SourceDiskResourceID: b.config.Source,
SubscriptionID: info.SubscriptionID,
Location: info.Location,
},
2019-06-03 04:33:31 -04:00
&StepCreateNewDisk{
SubscriptionID: info.SubscriptionID,
ResourceGroup: info.ResourceGroupName,
DiskName: b.config.TemporaryOSDiskName,
DiskSizeGB: b.config.OSDiskSizeGB,
DiskStorageAccountType: b.config.OSDiskStorageAccountType,
HyperVGeneration: b.config.ImageHyperVGeneration,
2019-09-10 08:48:55 -04:00
SourceDiskResourceID: b.config.Source,
2019-09-26 18:17:07 -04:00
Location: info.Location,
2019-06-03 19:06:19 -04:00
SkipCleanup: b.config.OSDiskSkipCleanup,
2019-06-03 04:33:31 -04:00
})
2019-09-10 08:48:55 -04:00
default:
panic(fmt.Errorf("Unknown source type: %+q", b.config.sourceType))
2019-06-03 04:33:31 -04:00
}
2019-05-27 02:20:11 -04:00
}
steps = append(steps,
2019-05-31 16:02:25 -04:00
&StepAttachDisk{}, // uses os_disk_resource_id and sets 'device' in stateBag
2019-05-31 14:39:43 -04:00
&chroot.StepPreMountCommands{
Commands: b.config.PreMountCommands,
},
2019-05-31 14:39:43 -04:00
&StepMountDevice{
MountOptions: b.config.MountOptions,
MountPartition: b.config.MountPartition,
MountPath: b.config.MountPath,
},
&chroot.StepPostMountCommands{
Commands: b.config.PostMountCommands,
},
2019-05-31 14:49:35 -04:00
&chroot.StepMountExtra{
ChrootMounts: b.config.ChrootMounts,
},
&chroot.StepCopyFiles{
Files: b.config.CopyFiles,
},
&chroot.StepChrootProvision{},
2019-05-31 15:01:47 -04:00
&chroot.StepEarlyCleanup{},
2019-05-31 16:02:25 -04:00
&StepCreateImage{
ImageResourceID: b.config.ImageResourceID,
2019-09-26 14:44:35 -04:00
ImageOSState: string(compute.Generalized),
2019-05-31 16:02:25 -04:00
OSDiskCacheType: b.config.OSDiskCacheType,
OSDiskStorageAccountType: b.config.OSDiskStorageAccountType,
2019-06-03 01:27:33 -04:00
Location: info.Location,
2019-05-31 16:02:25 -04:00
},
)
2019-05-16 20:24:53 -04:00
// Run!
b.runner = common.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)
}
2019-10-07 15:20:08 -04:00
// Build the artifact and return it
artifact := &azcommon.Artifact{
Resources: []string{b.config.ImageResourceID},
BuilderIdValue: BuilderId,
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
2019-10-07 15:20:08 -04:00
}
return artifact, nil
2019-05-16 20:24:53 -04:00
}
var _ packer.Builder = &Builder{}