packer-cn/builder/yandex/config.go

294 lines
10 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"
"github.com/yandex-cloud/go-sdk/iamkey"
)
const defaultEndpoint = "api.cloud.yandex.net:443"
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"`
// Non standard api endpoint URL.
2019-06-06 10:29:25 -04:00
Endpoint string `mapstructure:"endpoint" required:"false"`
// The folder ID that will be used to launch instances and store images.
2019-06-06 10:29:25 -04:00
// 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"`
// Path to file with Service Account key in json format. This
// is an alternative method to authenticate to Yandex.Cloud. Alternatively you may set environment variable
// YC_SERVICE_ACCOUNT_KEY_FILE.
ServiceAccountKeyFile string `mapstructure:"service_account_key_file" required:"false"`
2020-02-10 10:36:19 -05:00
// Service account identifier to assign to instance
ServiceAccountID string `mapstructure:"service_account_id" required:"false"`
// OAuth token to use to authenticate to Yandex.Cloud. Alternatively you may set
2019-06-06 10:29:25 -04:00
// value by environment variable YC_TOKEN.
Token string `mapstructure:"token" required:"true"`
// 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
2019-06-06 10:29:25 -04:00
// apply to the created image.
ImageLabels map[string]string `mapstructure:"image_labels" 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
2019-06-06 10:29:25 -04:00
// the launched instance.
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"`
// The maximum number of times an API request is being executed
MaxRetries int `mapstructure:"max_retries"`
// 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. Value are file paths.
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
2019-06-06 10:29:25 -04:00
// from.
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
}
var errs *packer.MultiError
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"
}
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.Endpoint == "" {
c.Endpoint = defaultEndpoint
2019-03-26 08:29:15 -04:00
}
if c.Zone == "" {
c.Zone = defaultZone
}
if c.MaxRetries == 0 {
c.MaxRetries = defaultMaxRetries
}
// provision config by OS environment variables
if c.Token == "" {
c.Token = os.Getenv("YC_TOKEN")
}
if c.ServiceAccountKeyFile == "" {
c.ServiceAccountKeyFile = os.Getenv("YC_SERVICE_ACCOUNT_KEY_FILE")
}
if c.FolderID == "" {
c.FolderID = os.Getenv("YC_FOLDER_ID")
2019-03-26 08:29:15 -04:00
}
if c.Token != "" && c.ServiceAccountKeyFile != "" {
errs = packer.MultiErrorAppend(
errs, errors.New("one of token or service account key file must be specified, not both"))
}
if c.Token != "" {
packer.LogSecretFilter.Set(c.Token)
}
if c.ServiceAccountKeyFile != "" {
if _, err := iamkey.ReadFromJSONFile(c.ServiceAccountKeyFile); err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("fail to read service account key file: %s", err))
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
}