packer-cn/builder/yandex/config.go

266 lines
9.3 KiB
Go
Raw Normal View History

//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type Config
2019-03-26 08:29:15 -04:00
package yandex
import (
"errors"
"fmt"
"os"
"regexp"
"time"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
)
const defaultGpuPlatformID = "gpu-standard-v1"
const defaultPlatformID = "standard-v1"
const defaultMaxRetries = 3
const defaultZone = "ru-central1-a"
2019-03-26 08:29:15 -04:00
var reImageFamily = regexp.MustCompile(`^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$`)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Communicator communicator.Config `mapstructure:",squash"`
AccessConfig `mapstructure:",squash"`
2019-03-26 08:29:15 -04:00
// The folder ID that will be used to launch instances and store images.
// Alternatively you may set value by environment variable `YC_FOLDER_ID`.
// To use a different folder for looking up the source image or saving the target image to
// check options 'source_image_folder_id' and 'target_image_folder_id'.
2019-06-06 10:29:25 -04:00
FolderID string `mapstructure:"folder_id" required:"true"`
// Service account identifier to assign to instance.
2020-02-10 10:36:19 -05:00
ServiceAccountID string `mapstructure:"service_account_id" required:"false"`
// The name of the disk, if unset the instance name
2019-06-06 10:29:25 -04:00
// will be used.
DiskName string `mapstructure:"disk_name" required:"false"`
// The size of the disk in GB. This defaults to `10`, which is 10GB.
2019-06-06 10:29:25 -04:00
DiskSizeGb int `mapstructure:"disk_size_gb" required:"false"`
// Specify disk type for the launched instance. Defaults to `network-hdd`.
2019-06-06 10:29:25 -04:00
DiskType string `mapstructure:"disk_type" required:"false"`
// The description of the resulting image.
2019-06-06 10:29:25 -04:00
ImageDescription string `mapstructure:"image_description" required:"false"`
// The family name of the resulting image.
2019-06-06 10:29:25 -04:00
ImageFamily string `mapstructure:"image_family" required:"false"`
// Key/value pair labels to apply to the created image.
2019-06-06 10:29:25 -04:00
ImageLabels map[string]string `mapstructure:"image_labels" required:"false"`
// Minimum size of the disk that will be created from built image, specified in gigabytes.
// Should be more or equal to `disk_size_gb`.
2020-07-16 10:29:45 -04:00
ImageMinDiskSizeGb int `mapstructure:"image_min_disk_size_gb" required:"false"`
// The unique name of the resulting image. Defaults to
// `packer-{{timestamp}}`.
2019-06-06 10:29:25 -04:00
ImageName string `mapstructure:"image_name" required:"false"`
// License IDs that indicate which licenses are attached to resulting image.
2019-06-06 10:29:25 -04:00
ImageProductIDs []string `mapstructure:"image_product_ids" required:"false"`
// The number of cores available to the instance.
2019-06-06 10:29:25 -04:00
InstanceCores int `mapstructure:"instance_cores" required:"false"`
// The number of GPU available to the instance.
InstanceGpus int `mapstructure:"instance_gpus"`
// The amount of memory available to the instance, specified in gigabytes.
2019-06-06 10:29:25 -04:00
InstanceMemory int `mapstructure:"instance_mem_gb" required:"false"`
// The name assigned to the instance.
2019-06-06 10:29:25 -04:00
InstanceName string `mapstructure:"instance_name" required:"false"`
// Key/value pair labels to apply to the launched instance.
2019-06-06 10:29:25 -04:00
Labels map[string]string `mapstructure:"labels" required:"false"`
// Identifier of the hardware platform configuration for the instance. This defaults to `standard-v1`.
2019-06-06 10:29:25 -04:00
PlatformID string `mapstructure:"platform_id" required:"false"`
// Metadata applied to the launched instance.
2019-06-06 10:29:25 -04:00
Metadata map[string]string `mapstructure:"metadata" required:"false"`
// Metadata applied to the launched instance.
// The values in this map are the paths to the content files for the corresponding metadata keys.
MetadataFromFile map[string]string `mapstructure:"metadata_from_file"`
// Launch a preemptible instance. This defaults to `false`.
Preemptible bool `mapstructure:"preemptible"`
// File path to save serial port output of the launched instance.
2019-06-06 10:29:25 -04:00
SerialLogFile string `mapstructure:"serial_log_file" required:"false"`
// The source image family to create the new image
2019-06-06 10:29:25 -04:00
// from. You can also specify source_image_id instead. Just one of a source_image_id or
// source_image_family must be specified. Example: `ubuntu-1804-lts`.
2019-06-06 10:29:25 -04:00
SourceImageFamily string `mapstructure:"source_image_family" required:"true"`
// The ID of the folder containing the source image.
2019-06-06 10:29:25 -04:00
SourceImageFolderID string `mapstructure:"source_image_folder_id" required:"false"`
// The source image ID to use to create the new image from.
2019-06-06 10:29:25 -04:00
SourceImageID string `mapstructure:"source_image_id" required:"false"`
// The source image name to use to create the new image
// from. Name will be looked up in `source_image_folder_id`.
SourceImageName string `mapstructure:"source_image_name"`
2019-06-06 10:29:25 -04:00
// The Yandex VPC subnet id to use for
// the launched instance. Note, the zone of the subnet must match the
// zone in which the VM is launched.
SubnetID string `mapstructure:"subnet_id" required:"false"`
// The ID of the folder to save built image in.
// This defaults to value of 'folder_id'.
TargetImageFolderID string `mapstructure:"target_image_folder_id" required:"false"`
2019-06-06 10:29:25 -04:00
// If set to true, then launched instance will have external internet
// access.
UseIPv4Nat bool `mapstructure:"use_ipv4_nat" required:"false"`
// Set to true to enable IPv6 for the instance being
// created. This defaults to `false`, or not enabled.
//
// -> **Note**: Usage of IPv6 will be available in the future.
2019-06-06 10:29:25 -04:00
UseIPv6 bool `mapstructure:"use_ipv6" required:"false"`
// If true, use the instance's internal IP address
2019-06-06 10:29:25 -04:00
// instead of its external IP during building.
UseInternalIP bool `mapstructure:"use_internal_ip" required:"false"`
// The name of the zone to launch the instance. This defaults to `ru-central1-a`.
2019-06-06 10:29:25 -04:00
Zone string `mapstructure:"zone" required:"false"`
2019-03-26 08:29:15 -04:00
2019-06-06 10:29:25 -04:00
ctx interpolate.Context
// The time to wait for instance state changes.
// Defaults to `5m`.
StateTimeout time.Duration `mapstructure:"state_timeout" required:"false"`
2019-03-26 08:29:15 -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
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
2019-03-26 08:29:15 -04:00
c.ctx.Funcs = TemplateFuncs
err := config.Decode(c, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &c.ctx,
}, raws...)
if err != nil {
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
return nil, err
2019-03-26 08:29:15 -04:00
}
// Accumulate any errors
2019-03-26 08:29:15 -04:00
var errs *packer.MultiError
errs = packer.MultiErrorAppend(errs, c.AccessConfig.Prepare(&c.ctx)...)
2019-03-26 08:29:15 -04:00
if c.SerialLogFile != "" {
if _, err := os.Stat(c.SerialLogFile); os.IsExist(err) {
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Serial log file %s already exist", c.SerialLogFile))
}
}
if c.InstanceCores == 0 {
c.InstanceCores = 2
}
if c.InstanceMemory == 0 {
c.InstanceMemory = 4
}
if c.DiskSizeGb == 0 {
c.DiskSizeGb = 10
}
if c.DiskType == "" {
c.DiskType = "network-hdd"
}
2020-07-16 10:29:45 -04:00
if c.ImageMinDiskSizeGb == 0 {
c.ImageMinDiskSizeGb = c.DiskSizeGb
}
2020-07-16 10:29:45 -04:00
if c.ImageMinDiskSizeGb < c.DiskSizeGb {
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Invalid image_min_disk_size value (%d): Must be equal or greate than disk_size_gb (%d)",
2020-07-16 10:29:45 -04:00
c.ImageMinDiskSizeGb, c.DiskSizeGb))
}
2019-03-26 08:29:15 -04:00
if c.ImageDescription == "" {
c.ImageDescription = "Created by Packer"
}
if c.ImageName == "" {
img, err := interpolate.Render("packer-{{timestamp}}", nil)
if err != nil {
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Unable to render default image name: %s ", err))
} else {
c.ImageName = img
}
}
if len(c.ImageFamily) > 63 {
errs = packer.MultiErrorAppend(errs,
errors.New("Invalid image family: Must not be longer than 63 characters"))
}
if c.ImageFamily != "" {
if !reImageFamily.MatchString(c.ImageFamily) {
errs = packer.MultiErrorAppend(errs,
errors.New("Invalid image family: The first character must be a "+
"lowercase letter, and all following characters must be a dash, "+
"lowercase letter, or digit, except the last character, which cannot be a dash"))
}
}
if c.InstanceName == "" {
c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
}
if c.DiskName == "" {
c.DiskName = c.InstanceName + "-disk"
}
if c.PlatformID == "" {
c.PlatformID = defaultPlatformID
if c.InstanceGpus != 0 {
c.PlatformID = defaultGpuPlatformID
}
2019-03-26 08:29:15 -04:00
}
if es := c.Communicator.Prepare(&c.ctx); len(es) > 0 {
errs = packer.MultiErrorAppend(errs, es...)
}
// Process required parameters.
if c.SourceImageID == "" {
if c.SourceImageFamily == "" && c.SourceImageName == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("a source_image_name or source_image_family must be specified"))
}
if c.SourceImageFamily != "" && c.SourceImageName != "" {
errs = packer.MultiErrorAppend(
errs, errors.New("one of source_image_name or source_image_family must be specified, not both"))
}
2019-03-26 08:29:15 -04:00
}
if c.Zone == "" {
c.Zone = defaultZone
}
if c.MaxRetries == 0 {
c.MaxRetries = defaultMaxRetries
}
if c.FolderID == "" {
c.FolderID = os.Getenv("YC_FOLDER_ID")
2019-03-26 08:29:15 -04:00
}
if c.FolderID == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("a folder_id must be specified"))
}
if c.TargetImageFolderID == "" {
c.TargetImageFolderID = c.FolderID
}
for key, file := range c.MetadataFromFile {
if _, err := os.Stat(file); err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("cannot access file '%s' with content for value of metadata key '%s': %s", file, key, err))
}
}
if c.StateTimeout == 0 {
c.StateTimeout = 5 * time.Minute
2019-03-26 08:29:15 -04:00
}
// Check for any errors.
if errs != nil && len(errs.Errors) > 0 {
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
return nil, errs
2019-03-26 08:29:15 -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
return nil, nil
2019-03-26 08:29:15 -04:00
}