diff --git a/builder/yandex/access_config.go b/builder/yandex/access_config.go index 413e4e617..a082a05fa 100644 --- a/builder/yandex/access_config.go +++ b/builder/yandex/access_config.go @@ -12,7 +12,10 @@ import ( "github.com/yandex-cloud/go-sdk/iamkey" ) -const defaultEndpoint = "api.cloud.yandex.net:443" +const ( + defaultEndpoint = "api.cloud.yandex.net:443" + defaultMaxRetries = 3 +) // AccessConfig is for common configuration related to Yandex.Cloud API access type AccessConfig struct { @@ -34,6 +37,10 @@ type AccessConfig struct { func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { var errs []error + if c.MaxRetries == 0 { + c.MaxRetries = defaultMaxRetries + } + if c.Endpoint == "" { c.Endpoint = defaultEndpoint } diff --git a/builder/yandex/builder.go b/builder/yandex/builder.go index 99f685c17..f481b9a77 100644 --- a/builder/yandex/builder.go +++ b/builder/yandex/builder.go @@ -87,7 +87,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) &commonsteps.StepCleanupTempKeys{ Comm: &b.config.Communicator, }, - &StepTeardownInstance{}, + &StepTeardownInstance{ + SerialLogFile: b.config.SerialLogFile, + }, &stepCreateImage{ GeneratedData: generatedData, }, diff --git a/builder/yandex/common_config.go b/builder/yandex/common_config.go new file mode 100644 index 000000000..8ff19c69d --- /dev/null +++ b/builder/yandex/common_config.go @@ -0,0 +1,234 @@ +//go:generate struct-markdown + +package yandex + +import ( + "errors" + "fmt" + "os" + "regexp" + "time" + + packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer" + "github.com/hashicorp/packer/packer-plugin-sdk/template/interpolate" + "github.com/hashicorp/packer/packer-plugin-sdk/uuid" +) + +const ( + defaultPlatformID = "standard-v2" + defaultZone = "ru-central1-a" + defaultGpuPlatformID = "gpu-standard-v1" +) + +var reImageFamily = regexp.MustCompile(`^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$`) + +type CommonConfig struct { + + // File path to save serial port output of the launched instance. + SerialLogFile string `mapstructure:"serial_log_file" required:"false"` + // The time to wait for instance state changes. + // Defaults to `5m`. + StateTimeout time.Duration `mapstructure:"state_timeout" required:"false"` + + InstanceConfig `mapstructure:",squash"` + DiskConfig `mapstructure:",squash"` + NetworkConfig `mapstructure:",squash"` + CloudConfig `mapstructure:",squash"` +} + +func (c *CommonConfig) Prepare(errs *packersdk.MultiError) *packersdk.MultiError { + + if c.SerialLogFile != "" { + if _, err := os.Stat(c.SerialLogFile); os.IsExist(err) { + errs = packersdk.MultiErrorAppend(errs, + fmt.Errorf("Serial log file %s already exist", c.SerialLogFile)) + } + } + + if c.StateTimeout == 0 { + c.StateTimeout = 5 * time.Minute + } + + errs = c.CloudConfig.Prepare(errs) + errs = c.InstanceConfig.Prepare(errs) + errs = c.DiskConfig.Prepare(errs) + errs = c.NetworkConfig.Prepare(errs) + + return errs +} + +type CloudConfig struct { + // 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'. + FolderID string `mapstructure:"folder_id" required:"true"` +} + +func (c *CloudConfig) Prepare(errs *packersdk.MultiError) *packersdk.MultiError { + if c.FolderID == "" { + c.FolderID = os.Getenv("YC_FOLDER_ID") + } + + if c.FolderID == "" { + errs = packersdk.MultiErrorAppend( + errs, errors.New("a folder_id must be specified")) + } + return errs +} + +type DiskConfig struct { + // The name of the disk, if unset the instance name + // will be used. + DiskName string `mapstructure:"disk_name" required:"false"` + // The size of the disk in GB. This defaults to 10/100GB. + DiskSizeGb int `mapstructure:"disk_size_gb" required:"false"` + // Specify disk type for the launched instance. Defaults to `network-ssd`. + DiskType string `mapstructure:"disk_type" required:"false"` + // Key/value pair labels to apply to the disk. + DiskLabels map[string]string `mapstructure:"disk_labels" required:"false"` +} + +func (c *DiskConfig) Prepare(errs *packersdk.MultiError) *packersdk.MultiError { + + if c.DiskSizeGb == 0 { + c.DiskSizeGb = 10 + } + + if c.DiskType == "" { + c.DiskType = "network-ssd" + } + + return errs +} + +type NetworkConfig struct { + // 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 name of the zone to launch the instance. This defaults to `ru-central1-a`. + Zone string `mapstructure:"zone" required:"false"` + + // 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. + UseIPv6 bool `mapstructure:"use_ipv6" required:"false"` + // If true, use the instance's internal IP address + // instead of its external IP during building. + UseInternalIP bool `mapstructure:"use_internal_ip" required:"false"` +} + +func (c *NetworkConfig) Prepare(errs *packersdk.MultiError) *packersdk.MultiError { + if c.Zone == "" { + c.Zone = defaultZone + } + + return errs +} + +type ImageConfig struct { + // The name of the resulting image, which contains 1-63 characters and only + // supports lowercase English characters, numbers and hyphen. Defaults to + // `packer-{{timestamp}}`. + ImageName string `mapstructure:"image_name" required:"false"` + // The description of the image. + ImageDescription string `mapstructure:"image_description" required:"false"` + // The family name of the image. + ImageFamily string `mapstructure:"image_family" required:"false"` + // Key/value pair labels to apply to the image. + 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`. + ImageMinDiskSizeGb int `mapstructure:"image_min_disk_size_gb" required:"false"` + // License IDs that indicate which licenses are attached to resulting image. + ImageProductIDs []string `mapstructure:"image_product_ids" required:"false"` +} + +func (c *ImageConfig) Prepare(errs *packersdk.MultiError) *packersdk.MultiError { + + if len(c.ImageFamily) > 63 { + errs = packersdk.MultiErrorAppend(errs, + errors.New("Invalid image family: Must not be longer than 63 characters")) + } + + if c.ImageFamily != "" { + if !reImageFamily.MatchString(c.ImageFamily) { + errs = packersdk.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.ImageDescription == "" { + c.ImageDescription = "Created by Packer" + } + + if c.ImageName == "" { + img, err := interpolate.Render("packer-{{timestamp}}", nil) + if err != nil { + errs = packersdk.MultiErrorAppend(errs, + fmt.Errorf("Unable to render default image name: %s ", err)) + } else { + c.ImageName = img + } + } + + return errs +} + +type InstanceConfig struct { + // The number of cores available to the instance. + InstanceCores int `mapstructure:"instance_cores" required:"false"` + // The number of GPU available to the instance. + InstanceGpus int `mapstructure:"instance_gpus" required:"false"` + // The amount of memory available to the instance, specified in gigabytes. + InstanceMemory int `mapstructure:"instance_mem_gb" required:"false"` + // The name assigned to the instance. + InstanceName string `mapstructure:"instance_name" required:"false"` + // Identifier of the hardware platform configuration for the instance. This defaults to `standard-v2`. + PlatformID string `mapstructure:"platform_id" required:"false"` + // Key/value pair labels to apply to the launched instance. + Labels map[string]string `mapstructure:"labels" required:"false"` + // Metadata applied to the launched instance. + 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"` +} + +func (c *InstanceConfig) Prepare(errs *packersdk.MultiError) *packersdk.MultiError { + if c.InstanceCores == 0 { + c.InstanceCores = 2 + } + + if c.InstanceMemory == 0 { + c.InstanceMemory = 4 + } + + if c.InstanceName == "" { + c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) + } + + for key, file := range c.MetadataFromFile { + if _, err := os.Stat(file); err != nil { + errs = packersdk.MultiErrorAppend( + errs, fmt.Errorf("cannot access file '%s' with content for value of metadata key '%s': %s", file, key, err)) + } + } + + if c.PlatformID == "" { + c.PlatformID = defaultPlatformID + if c.InstanceGpus != 0 { + c.PlatformID = defaultGpuPlatformID + } + } + return errs +} diff --git a/builder/yandex/config.go b/builder/yandex/config.go index bcb883029..fb54bbcab 100644 --- a/builder/yandex/config.go +++ b/builder/yandex/config.go @@ -6,81 +6,25 @@ package yandex import ( "errors" "fmt" - "os" - "regexp" - "time" "github.com/hashicorp/packer/packer-plugin-sdk/common" "github.com/hashicorp/packer/packer-plugin-sdk/communicator" packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer" "github.com/hashicorp/packer/packer-plugin-sdk/template/config" "github.com/hashicorp/packer/packer-plugin-sdk/template/interpolate" - "github.com/hashicorp/packer/packer-plugin-sdk/uuid" ) -const defaultGpuPlatformID = "gpu-standard-v1" -const defaultPlatformID = "standard-v1" -const defaultMaxRetries = 3 -const defaultZone = "ru-central1-a" - -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"` - // 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'. - FolderID string `mapstructure:"folder_id" required:"true"` + CommonConfig `mapstructure:",squash"` + ImageConfig `mapstructure:",squash"` + // Service account identifier to assign to instance. ServiceAccountID string `mapstructure:"service_account_id" required:"false"` - // The name of the disk, if unset the instance name - // will be used. - DiskName string `mapstructure:"disk_name" required:"false"` - // The size of the disk in GB. This defaults to `10`, which is 10GB. - DiskSizeGb int `mapstructure:"disk_size_gb" required:"false"` - // Specify disk type for the launched instance. Defaults to `network-hdd`. - DiskType string `mapstructure:"disk_type" required:"false"` - // Key/value pair labels to apply to the disk. - DiskLabels map[string]string `mapstructure:"disk_labels" required:"false"` - // The description of the resulting image. - ImageDescription string `mapstructure:"image_description" required:"false"` - // The family name of the resulting image. - ImageFamily string `mapstructure:"image_family" required:"false"` - // Key/value pair labels to apply to the created image. - 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`. - ImageMinDiskSizeGb int `mapstructure:"image_min_disk_size_gb" required:"false"` - // The unique name of the resulting image. Defaults to - // `packer-{{timestamp}}`. - ImageName string `mapstructure:"image_name" required:"false"` - // License IDs that indicate which licenses are attached to resulting image. - ImageProductIDs []string `mapstructure:"image_product_ids" required:"false"` - // The number of cores available to the instance. - 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. - InstanceMemory int `mapstructure:"instance_mem_gb" required:"false"` - // The name assigned to the instance. - InstanceName string `mapstructure:"instance_name" required:"false"` - // Key/value pair labels to apply to 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`. - PlatformID string `mapstructure:"platform_id" required:"false"` - // Metadata applied to the launched instance. - 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. - SerialLogFile string `mapstructure:"serial_log_file" required:"false"` + // The source image family to create the new image // 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`. @@ -92,31 +36,11 @@ type Config struct { // 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"` - // 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"` - // 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. - UseIPv6 bool `mapstructure:"use_ipv6" required:"false"` - // If true, use the instance's internal IP address - // 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`. - Zone string `mapstructure:"zone" required:"false"` ctx interpolate.Context - // The time to wait for instance state changes. - // Defaults to `5m`. - StateTimeout time.Duration `mapstructure:"state_timeout" required:"false"` } func (c *Config) Prepare(raws ...interface{}) ([]string, error) { @@ -134,28 +58,8 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { errs = packersdk.MultiErrorAppend(errs, c.AccessConfig.Prepare(&c.ctx)...) - if c.SerialLogFile != "" { - if _, err := os.Stat(c.SerialLogFile); os.IsExist(err) { - errs = packersdk.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" - } + errs = c.CommonConfig.Prepare(errs) + errs = c.ImageConfig.Prepare(errs) if c.ImageMinDiskSizeGb == 0 { c.ImageMinDiskSizeGb = c.DiskSizeGb @@ -167,49 +71,10 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { c.ImageMinDiskSizeGb, c.DiskSizeGb)) } - if c.ImageDescription == "" { - c.ImageDescription = "Created by Packer" - } - - if c.ImageName == "" { - img, err := interpolate.Render("packer-{{timestamp}}", nil) - if err != nil { - errs = packersdk.MultiErrorAppend(errs, - fmt.Errorf("Unable to render default image name: %s ", err)) - } else { - c.ImageName = img - } - } - - if len(c.ImageFamily) > 63 { - errs = packersdk.MultiErrorAppend(errs, - errors.New("Invalid image family: Must not be longer than 63 characters")) - } - - if c.ImageFamily != "" { - if !reImageFamily.MatchString(c.ImageFamily) { - errs = packersdk.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 - } - } - if es := c.Communicator.Prepare(&c.ctx); len(es) > 0 { errs = packersdk.MultiErrorAppend(errs, es...) } @@ -226,38 +91,10 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { } } - if c.Zone == "" { - c.Zone = defaultZone - } - - if c.MaxRetries == 0 { - c.MaxRetries = defaultMaxRetries - } - - if c.FolderID == "" { - c.FolderID = os.Getenv("YC_FOLDER_ID") - } - - if c.FolderID == "" { - errs = packersdk.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 = packersdk.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 - } - // Check for any errors. if errs != nil && len(errs.Errors) > 0 { return nil, errs diff --git a/builder/yandex/config.hcl2spec.go b/builder/yandex/config.hcl2spec.go index 4c1086481..3e0ec7661 100644 --- a/builder/yandex/config.hcl2spec.go +++ b/builder/yandex/config.hcl2spec.go @@ -70,39 +70,39 @@ type FlatConfig struct { ServiceAccountKeyFile *string `mapstructure:"service_account_key_file" required:"false" cty:"service_account_key_file" hcl:"service_account_key_file"` Token *string `mapstructure:"token" required:"true" cty:"token" hcl:"token"` MaxRetries *int `mapstructure:"max_retries" cty:"max_retries" hcl:"max_retries"` - FolderID *string `mapstructure:"folder_id" required:"true" cty:"folder_id" hcl:"folder_id"` - ServiceAccountID *string `mapstructure:"service_account_id" required:"false" cty:"service_account_id" hcl:"service_account_id"` + SerialLogFile *string `mapstructure:"serial_log_file" required:"false" cty:"serial_log_file" hcl:"serial_log_file"` + StateTimeout *string `mapstructure:"state_timeout" required:"false" cty:"state_timeout" hcl:"state_timeout"` + InstanceCores *int `mapstructure:"instance_cores" required:"false" cty:"instance_cores" hcl:"instance_cores"` + InstanceGpus *int `mapstructure:"instance_gpus" required:"false" cty:"instance_gpus" hcl:"instance_gpus"` + InstanceMemory *int `mapstructure:"instance_mem_gb" required:"false" cty:"instance_mem_gb" hcl:"instance_mem_gb"` + InstanceName *string `mapstructure:"instance_name" required:"false" cty:"instance_name" hcl:"instance_name"` + PlatformID *string `mapstructure:"platform_id" required:"false" cty:"platform_id" hcl:"platform_id"` + Labels map[string]string `mapstructure:"labels" required:"false" cty:"labels" hcl:"labels"` + Metadata map[string]string `mapstructure:"metadata" required:"false" cty:"metadata" hcl:"metadata"` + MetadataFromFile map[string]string `mapstructure:"metadata_from_file" cty:"metadata_from_file" hcl:"metadata_from_file"` + Preemptible *bool `mapstructure:"preemptible" cty:"preemptible" hcl:"preemptible"` DiskName *string `mapstructure:"disk_name" required:"false" cty:"disk_name" hcl:"disk_name"` DiskSizeGb *int `mapstructure:"disk_size_gb" required:"false" cty:"disk_size_gb" hcl:"disk_size_gb"` DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type" hcl:"disk_type"` DiskLabels map[string]string `mapstructure:"disk_labels" required:"false" cty:"disk_labels" hcl:"disk_labels"` + SubnetID *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"` + Zone *string `mapstructure:"zone" required:"false" cty:"zone" hcl:"zone"` + UseIPv4Nat *bool `mapstructure:"use_ipv4_nat" required:"false" cty:"use_ipv4_nat" hcl:"use_ipv4_nat"` + UseIPv6 *bool `mapstructure:"use_ipv6" required:"false" cty:"use_ipv6" hcl:"use_ipv6"` + UseInternalIP *bool `mapstructure:"use_internal_ip" required:"false" cty:"use_internal_ip" hcl:"use_internal_ip"` + FolderID *string `mapstructure:"folder_id" required:"true" cty:"folder_id" hcl:"folder_id"` + ImageName *string `mapstructure:"image_name" required:"false" cty:"image_name" hcl:"image_name"` ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description" hcl:"image_description"` ImageFamily *string `mapstructure:"image_family" required:"false" cty:"image_family" hcl:"image_family"` ImageLabels map[string]string `mapstructure:"image_labels" required:"false" cty:"image_labels" hcl:"image_labels"` ImageMinDiskSizeGb *int `mapstructure:"image_min_disk_size_gb" required:"false" cty:"image_min_disk_size_gb" hcl:"image_min_disk_size_gb"` - ImageName *string `mapstructure:"image_name" required:"false" cty:"image_name" hcl:"image_name"` ImageProductIDs []string `mapstructure:"image_product_ids" required:"false" cty:"image_product_ids" hcl:"image_product_ids"` - InstanceCores *int `mapstructure:"instance_cores" required:"false" cty:"instance_cores" hcl:"instance_cores"` - InstanceGpus *int `mapstructure:"instance_gpus" cty:"instance_gpus" hcl:"instance_gpus"` - InstanceMemory *int `mapstructure:"instance_mem_gb" required:"false" cty:"instance_mem_gb" hcl:"instance_mem_gb"` - InstanceName *string `mapstructure:"instance_name" required:"false" cty:"instance_name" hcl:"instance_name"` - Labels map[string]string `mapstructure:"labels" required:"false" cty:"labels" hcl:"labels"` - PlatformID *string `mapstructure:"platform_id" required:"false" cty:"platform_id" hcl:"platform_id"` - Metadata map[string]string `mapstructure:"metadata" required:"false" cty:"metadata" hcl:"metadata"` - MetadataFromFile map[string]string `mapstructure:"metadata_from_file" cty:"metadata_from_file" hcl:"metadata_from_file"` - Preemptible *bool `mapstructure:"preemptible" cty:"preemptible" hcl:"preemptible"` - SerialLogFile *string `mapstructure:"serial_log_file" required:"false" cty:"serial_log_file" hcl:"serial_log_file"` + ServiceAccountID *string `mapstructure:"service_account_id" required:"false" cty:"service_account_id" hcl:"service_account_id"` SourceImageFamily *string `mapstructure:"source_image_family" required:"true" cty:"source_image_family" hcl:"source_image_family"` SourceImageFolderID *string `mapstructure:"source_image_folder_id" required:"false" cty:"source_image_folder_id" hcl:"source_image_folder_id"` SourceImageID *string `mapstructure:"source_image_id" required:"false" cty:"source_image_id" hcl:"source_image_id"` SourceImageName *string `mapstructure:"source_image_name" cty:"source_image_name" hcl:"source_image_name"` - SubnetID *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"` TargetImageFolderID *string `mapstructure:"target_image_folder_id" required:"false" cty:"target_image_folder_id" hcl:"target_image_folder_id"` - UseIPv4Nat *bool `mapstructure:"use_ipv4_nat" required:"false" cty:"use_ipv4_nat" hcl:"use_ipv4_nat"` - UseIPv6 *bool `mapstructure:"use_ipv6" required:"false" cty:"use_ipv6" hcl:"use_ipv6"` - UseInternalIP *bool `mapstructure:"use_internal_ip" required:"false" cty:"use_internal_ip" hcl:"use_internal_ip"` - Zone *string `mapstructure:"zone" required:"false" cty:"zone" hcl:"zone"` - StateTimeout *string `mapstructure:"state_timeout" required:"false" cty:"state_timeout" hcl:"state_timeout"` } // FlatMapstructure returns a new FlatConfig. @@ -178,39 +178,39 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "service_account_key_file": &hcldec.AttrSpec{Name: "service_account_key_file", Type: cty.String, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false}, - "folder_id": &hcldec.AttrSpec{Name: "folder_id", Type: cty.String, Required: false}, - "service_account_id": &hcldec.AttrSpec{Name: "service_account_id", Type: cty.String, Required: false}, - "disk_name": &hcldec.AttrSpec{Name: "disk_name", Type: cty.String, Required: false}, - "disk_size_gb": &hcldec.AttrSpec{Name: "disk_size_gb", Type: cty.Number, Required: false}, - "disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false}, - "disk_labels": &hcldec.AttrSpec{Name: "disk_labels", Type: cty.Map(cty.String), Required: false}, - "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, - "image_family": &hcldec.AttrSpec{Name: "image_family", Type: cty.String, Required: false}, - "image_labels": &hcldec.AttrSpec{Name: "image_labels", Type: cty.Map(cty.String), Required: false}, - "image_min_disk_size_gb": &hcldec.AttrSpec{Name: "image_min_disk_size_gb", Type: cty.Number, Required: false}, - "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, - "image_product_ids": &hcldec.AttrSpec{Name: "image_product_ids", Type: cty.List(cty.String), Required: false}, + "serial_log_file": &hcldec.AttrSpec{Name: "serial_log_file", Type: cty.String, Required: false}, + "state_timeout": &hcldec.AttrSpec{Name: "state_timeout", Type: cty.String, Required: false}, "instance_cores": &hcldec.AttrSpec{Name: "instance_cores", Type: cty.Number, Required: false}, "instance_gpus": &hcldec.AttrSpec{Name: "instance_gpus", Type: cty.Number, Required: false}, "instance_mem_gb": &hcldec.AttrSpec{Name: "instance_mem_gb", Type: cty.Number, Required: false}, "instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false}, - "labels": &hcldec.AttrSpec{Name: "labels", Type: cty.Map(cty.String), Required: false}, "platform_id": &hcldec.AttrSpec{Name: "platform_id", Type: cty.String, Required: false}, + "labels": &hcldec.AttrSpec{Name: "labels", Type: cty.Map(cty.String), Required: false}, "metadata": &hcldec.AttrSpec{Name: "metadata", Type: cty.Map(cty.String), Required: false}, "metadata_from_file": &hcldec.AttrSpec{Name: "metadata_from_file", Type: cty.Map(cty.String), Required: false}, "preemptible": &hcldec.AttrSpec{Name: "preemptible", Type: cty.Bool, Required: false}, - "serial_log_file": &hcldec.AttrSpec{Name: "serial_log_file", Type: cty.String, Required: false}, + "disk_name": &hcldec.AttrSpec{Name: "disk_name", Type: cty.String, Required: false}, + "disk_size_gb": &hcldec.AttrSpec{Name: "disk_size_gb", Type: cty.Number, Required: false}, + "disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false}, + "disk_labels": &hcldec.AttrSpec{Name: "disk_labels", Type: cty.Map(cty.String), Required: false}, + "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "zone": &hcldec.AttrSpec{Name: "zone", Type: cty.String, Required: false}, + "use_ipv4_nat": &hcldec.AttrSpec{Name: "use_ipv4_nat", Type: cty.Bool, Required: false}, + "use_ipv6": &hcldec.AttrSpec{Name: "use_ipv6", Type: cty.Bool, Required: false}, + "use_internal_ip": &hcldec.AttrSpec{Name: "use_internal_ip", Type: cty.Bool, Required: false}, + "folder_id": &hcldec.AttrSpec{Name: "folder_id", Type: cty.String, Required: false}, + "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, + "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, + "image_family": &hcldec.AttrSpec{Name: "image_family", Type: cty.String, Required: false}, + "image_labels": &hcldec.AttrSpec{Name: "image_labels", Type: cty.Map(cty.String), Required: false}, + "image_min_disk_size_gb": &hcldec.AttrSpec{Name: "image_min_disk_size_gb", Type: cty.Number, Required: false}, + "image_product_ids": &hcldec.AttrSpec{Name: "image_product_ids", Type: cty.List(cty.String), Required: false}, + "service_account_id": &hcldec.AttrSpec{Name: "service_account_id", Type: cty.String, Required: false}, "source_image_family": &hcldec.AttrSpec{Name: "source_image_family", Type: cty.String, Required: false}, "source_image_folder_id": &hcldec.AttrSpec{Name: "source_image_folder_id", Type: cty.String, Required: false}, "source_image_id": &hcldec.AttrSpec{Name: "source_image_id", Type: cty.String, Required: false}, "source_image_name": &hcldec.AttrSpec{Name: "source_image_name", Type: cty.String, Required: false}, - "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, "target_image_folder_id": &hcldec.AttrSpec{Name: "target_image_folder_id", Type: cty.String, Required: false}, - "use_ipv4_nat": &hcldec.AttrSpec{Name: "use_ipv4_nat", Type: cty.Bool, Required: false}, - "use_ipv6": &hcldec.AttrSpec{Name: "use_ipv6", Type: cty.Bool, Required: false}, - "use_internal_ip": &hcldec.AttrSpec{Name: "use_internal_ip", Type: cty.Bool, Required: false}, - "zone": &hcldec.AttrSpec{Name: "zone", Type: cty.String, Required: false}, - "state_timeout": &hcldec.AttrSpec{Name: "state_timeout", Type: cty.String, Required: false}, } return s } diff --git a/builder/yandex/step_create_instance.go b/builder/yandex/step_create_instance.go index c94737555..b61abba06 100644 --- a/builder/yandex/step_create_instance.go +++ b/builder/yandex/step_create_instance.go @@ -56,7 +56,7 @@ func createNetwork(ctx context.Context, c *Config, d Driver) (*vpc.Network, erro return network, nil } -func createDisk(ctx context.Context, c *Config, d Driver, sourceImage *Image) (*compute.Disk, error) { +func createDisk(ctx context.Context, state multistep.StateBag, c *Config, d Driver, sourceImage *Image) (*compute.Disk, error) { req := &compute.CreateDiskRequest{ Name: c.DiskName, FolderId: c.FolderID, @@ -74,6 +74,16 @@ func createDisk(ctx context.Context, c *Config, d Driver, sourceImage *Image) (* if err != nil { return nil, err } + protoMD, err := op.Metadata() + if err != nil { + return nil, err + } + md, ok := protoMD.(*compute.CreateDiskMetadata) + if !ok { + return nil, fmt.Errorf("could not get Instance ID from create operation metadata") + } + state.Put("disk_id", md.DiskId) + err = op.Wait(ctx) if err != nil { return nil, err @@ -83,11 +93,11 @@ func createDisk(ctx context.Context, c *Config, d Driver, sourceImage *Image) (* return nil, err } - image, ok := resp.(*compute.Disk) + disk, ok := resp.(*compute.Disk) if !ok { return nil, errors.New("disk create operation response doesn't contain Disk") } - return image, nil + return disk, nil } @@ -191,11 +201,10 @@ func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag) // Create a disk manually to have a delete ID ui.Say("Creating disk...") - disk, err := createDisk(ctx, config, driver, sourceImage) + disk, err := createDisk(ctx, state, config, driver, sourceImage) if err != nil { return stepHaltWithError(state, fmt.Errorf("Error creating disk: %s", err)) } - state.Put("disk_id", disk.Id) // Create an instance based on the configuration ui.Say("Creating instance...") @@ -331,7 +340,7 @@ func (s *StepCreateInstance) Cleanup(state multistep.StateBag) { if s.SerialLogFile != "" { ui.Say("Current state 'cancelled' or 'halted'...") - err := s.writeSerialLogFile(ctx, state) + err := writeSerialLogFile(ctx, state, s.SerialLogFile) if err != nil { ui.Error(err.Error()) } @@ -395,25 +404,6 @@ func (s *StepCreateInstance) Cleanup(state multistep.StateBag) { } } -func (s *StepCreateInstance) writeSerialLogFile(ctx context.Context, state multistep.StateBag) error { - sdk := state.Get("sdk").(*ycsdk.SDK) - ui := state.Get("ui").(packersdk.Ui) - - instanceID := state.Get("instance_id").(string) - ui.Say("Try get instance's serial port output and write to file " + s.SerialLogFile) - serialOutput, err := sdk.Compute().Instance().GetSerialPortOutput(ctx, &compute.GetInstanceSerialPortOutputRequest{ - InstanceId: instanceID, - }) - if err != nil { - return fmt.Errorf("Failed to get serial port output for instance (id: %s): %s", instanceID, err) - } - if err := ioutil.WriteFile(s.SerialLogFile, []byte(serialOutput.Contents), 0600); err != nil { - return fmt.Errorf("Failed to write serial port output to file: %s", err) - } - ui.Message("Serial port output has been successfully written") - return nil -} - func (c *Config) createInstanceMetadata(sshPublicKey string) (map[string]string, error) { instanceMetadata := make(map[string]string) diff --git a/builder/yandex/step_teardown_instance.go b/builder/yandex/step_teardown_instance.go index 0c10b0673..2845fba85 100644 --- a/builder/yandex/step_teardown_instance.go +++ b/builder/yandex/step_teardown_instance.go @@ -11,7 +11,9 @@ import ( ycsdk "github.com/yandex-cloud/go-sdk" ) -type StepTeardownInstance struct{} +type StepTeardownInstance struct { + SerialLogFile string +} func (s *StepTeardownInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { sdk := state.Get("sdk").(*ycsdk.SDK) @@ -23,6 +25,14 @@ func (s *StepTeardownInstance) Run(ctx context.Context, state multistep.StateBag ui.Say("Stopping instance...") ctx, cancel := context.WithTimeout(ctx, c.StateTimeout) defer cancel() + + if s.SerialLogFile != "" { + err := writeSerialLogFile(ctx, state, s.SerialLogFile) + if err != nil { + ui.Error(err.Error()) + } + } + op, err := sdk.WrapOperation(sdk.Compute().Instance().Stop(ctx, &compute.StopInstanceRequest{ InstanceId: instanceID, })) diff --git a/builder/yandex/step_wait_cloudinit_script.go b/builder/yandex/step_wait_cloudinit_script.go index 35479f794..75c4faa37 100644 --- a/builder/yandex/step_wait_cloudinit_script.go +++ b/builder/yandex/step_wait_cloudinit_script.go @@ -19,7 +19,7 @@ type StepWaitCloudInitScript int // Run reads the instance metadata and looks for the log entry // indicating the cloud-init script finished. -func (s *StepWaitCloudInitScript) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { +func (*StepWaitCloudInitScript) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { _ = state.Get("config").(*Config) driver := state.Get("driver").(Driver) ui := state.Get("ui").(packersdk.Ui) diff --git a/builder/yandex/util.go b/builder/yandex/util.go index 42d2082a4..fbe74ec3e 100644 --- a/builder/yandex/util.go +++ b/builder/yandex/util.go @@ -1,9 +1,15 @@ package yandex import ( + "context" + "fmt" + "io/ioutil" + "github.com/c2h5oh/datasize" "github.com/hashicorp/packer/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer" + "github.com/yandex-cloud/go-genproto/yandex/cloud/compute/v1" + ycsdk "github.com/yandex-cloud/go-sdk" ) func stepHaltWithError(state multistep.StateBag, err error) multistep.StepAction { @@ -20,3 +26,25 @@ func toGigabytes(bytesCount int64) int { func toBytes(gigabytesCount int) int64 { return int64((datasize.ByteSize(gigabytesCount) * datasize.GB).Bytes()) } + +func writeSerialLogFile(ctx context.Context, state multistep.StateBag, serialLogFile string) error { + sdk := state.Get("sdk").(*ycsdk.SDK) + ui := state.Get("ui").(packersdk.Ui) + + instanceID := state.Get("instance_id").(string) + if instanceID == "" { + return nil + } + ui.Say("Try get instance's serial port output and write to file " + serialLogFile) + serialOutput, err := sdk.Compute().Instance().GetSerialPortOutput(ctx, &compute.GetInstanceSerialPortOutputRequest{ + InstanceId: instanceID, + }) + if err != nil { + return fmt.Errorf("Failed to get serial port output for instance (id: %s): %s", instanceID, err) + } + if err := ioutil.WriteFile(serialLogFile, []byte(serialOutput.Contents), 0600); err != nil { + return fmt.Errorf("Failed to write serial port output to file: %s", err) + } + ui.Message("Serial port output has been successfully written") + return nil +} diff --git a/post-processor/yandex-export/config.go b/post-processor/yandex-export/config.go new file mode 100644 index 000000000..ce71f1ba2 --- /dev/null +++ b/post-processor/yandex-export/config.go @@ -0,0 +1,23 @@ +//go:generate struct-markdown +package yandexexport + +import ( + "fmt" + + packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer" +) + +type ExchangeConfig struct { + // Service Account ID with proper permission to modify an instance, create and attach disk and + // make upload to specific Yandex Object Storage paths. + ServiceAccountID string `mapstructure:"service_account_id" required:"true"` +} + +func (c *ExchangeConfig) Prepare(errs *packersdk.MultiError) *packersdk.MultiError { + if c.ServiceAccountID == "" { + errs = packersdk.MultiErrorAppend( + errs, fmt.Errorf("service_account_id must be specified")) + } + + return errs +} diff --git a/post-processor/yandex-export/post-processor.go b/post-processor/yandex-export/post-processor.go index f1fa38b31..352158f86 100644 --- a/post-processor/yandex-export/post-processor.go +++ b/post-processor/yandex-export/post-processor.go @@ -8,13 +8,12 @@ import ( "context" "fmt" "log" - "os" "strings" - "time" "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/yandex" "github.com/hashicorp/packer/packer-plugin-sdk/common" + "github.com/hashicorp/packer/packer-plugin-sdk/communicator" "github.com/hashicorp/packer/packer-plugin-sdk/multistep" "github.com/hashicorp/packer/packer-plugin-sdk/multistep/commonsteps" packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer" @@ -32,6 +31,8 @@ const defaultStorageEndpoint = "storage.yandexcloud.net" type Config struct { common.PackerConfig `mapstructure:",squash"` yandex.AccessConfig `mapstructure:",squash"` + yandex.CommonConfig `mapstructure:",squash"` + ExchangeConfig `mapstructure:",squash"` // List of paths to Yandex Object Storage where exported image will be uploaded. // Please be aware that use of space char inside path not supported. @@ -39,24 +40,11 @@ type Config struct { // Check available template data for [Yandex](/docs/builders/yandex#build-template-data) builder. // Paths to Yandex Object Storage where exported image will be uploaded. Paths []string `mapstructure:"paths" required:"true"` - // The folder ID that will be used to launch a temporary instance. - // Alternatively you may set value by environment variable `YC_FOLDER_ID`. - FolderID string `mapstructure:"folder_id" required:"true"` - // Service Account ID with proper permission to modify an instance, create and attach disk and - // make upload to specific Yandex Object Storage paths. - ServiceAccountID string `mapstructure:"service_account_id" required:"true"` - // The size of the disk in GB. This defaults to `100`, which is 100GB. - DiskSizeGb int `mapstructure:"disk_size" required:"false"` - // Specify disk type for the launched instance. Defaults to `network-ssd`. - DiskType string `mapstructure:"disk_type" required:"false"` - // Identifier of the hardware platform configuration for the instance. This defaults to `standard-v2`. - PlatformID string `mapstructure:"platform_id" required:"false"` - // 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 name of the zone to launch the instance. This defaults to `ru-central1-a`. - Zone string `mapstructure:"zone" required:"false"` + + // Path to a PEM encoded private key file to use to authenticate with SSH. + // The `~` can be used in path and will be expanded to the home directory + // of current user. Login for attach: `ubuntu` + SSHPrivateKeyFile string `mapstructure:"ssh_private_key_file" required:"false"` ctx interpolate.Context } @@ -88,6 +76,14 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { errs = packersdk.MultiErrorAppend(errs, p.config.AccessConfig.Prepare(&p.config.ctx)...) + // Set defaults. + if p.config.DiskSizeGb == 0 { + p.config.DiskSizeGb = 100 + } + + errs = p.config.CommonConfig.Prepare(errs) + errs = p.config.ExchangeConfig.Prepare(errs) + if len(p.config.Paths) == 0 { errs = packersdk.MultiErrorAppend( errs, fmt.Errorf("paths must be specified")) @@ -101,31 +97,29 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { } } - if p.config.FolderID == "" { - p.config.FolderID = os.Getenv("YC_FOLDER_ID") - } - - // Set defaults. - if p.config.DiskSizeGb == 0 { - p.config.DiskSizeGb = 100 - } - - if p.config.DiskType == "" { - p.config.DiskType = "network-ssd" - } - - if p.config.PlatformID == "" { - p.config.PlatformID = "standard-v2" - } - - if p.config.Zone == "" { - p.config.Zone = "ru-central1-a" - } - if len(errs.Errors) > 0 { return errs } + // Due to the fact that now it's impossible to go to the object storage + // through the internal network - we need access + // to the global Internet: either through ipv4 or ipv6 + // TODO: delete this when access appears + if p.config.UseIPv4Nat == false && p.config.UseIPv6 == false { + p.config.UseIPv4Nat = true + } + p.config.Preemptible = true //? safety + + if p.config.Labels == nil { + p.config.Labels = make(map[string]string) + } + if _, ok := p.config.Labels["role"]; !ok { + p.config.Labels["role"] = "exporter" + } + if _, ok := p.config.Labels["target"]; !ok { + p.config.Labels["target"] = "object-storage" + } + return nil } @@ -168,15 +162,6 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa imageID := artifact.State("ImageID").(string) ui.Say(fmt.Sprintf("Exporting image %v to destination: %v", imageID, p.config.Paths)) - // Set up exporter instance configuration. - exporterName := fmt.Sprintf("%s-exporter", artifact.Id()) - exporterMetadata := map[string]string{ - "image_id": imageID, - "name": exporterName, - "paths": strings.Join(p.config.Paths, " "), - "user-data": CloudInitScript, - "zone": p.config.Zone, - } driver, err := yandex.NewDriverYC(ui, &p.config.AccessConfig) if err != nil { return nil, false, false, err @@ -202,23 +187,29 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa }, }, } - yandexConfig := ycSaneDefaults() - yandexConfig.DiskName = exporterName - yandexConfig.InstanceName = exporterName - yandexConfig.DiskSizeGb = p.config.DiskSizeGb - yandexConfig.Metadata = exporterMetadata - yandexConfig.SubnetID = p.config.SubnetID - yandexConfig.FolderID = p.config.FolderID - yandexConfig.Zone = p.config.Zone - - if p.config.ServiceAccountID != "" { - yandexConfig.ServiceAccountID = p.config.ServiceAccountID + // Set up exporter instance configuration. + exporterName := fmt.Sprintf("%s-exporter", artifact.Id()) + yandexConfig := ycSaneDefaults(&p.config, + map[string]string{ + "image_id": imageID, + "name": exporterName, + "paths": strings.Join(p.config.Paths, " "), + "user-data": CloudInitScript, + "zone": p.config.Zone, + }, + ) + if yandexConfig.InstanceConfig.InstanceName == "" { + yandexConfig.InstanceConfig.InstanceName = exporterName + } + if yandexConfig.DiskName == "" { + yandexConfig.DiskName = exporterName } - if p.config.PlatformID != "" { - yandexConfig.PlatformID = p.config.PlatformID + errs := yandexConfig.Communicator.Prepare(interpolate.NewContext()) + if len(errs) > 0 { + err := &packersdk.MultiError{Errors: errs} + return nil, false, false, err } - ui.Say(fmt.Sprintf("Validating service_account_id: '%s'...", yandexConfig.ServiceAccountID)) if err := validateServiceAccount(ctx, driver.SDK(), yandexConfig.ServiceAccountID); err != nil { return nil, false, false, err @@ -240,10 +231,13 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa }, &yandex.StepCreateInstance{ Debug: p.config.PackerDebug, + SerialLogFile: yandexConfig.SerialLogFile, GeneratedData: &packerbuilderdata.GeneratedData{State: state}, }, new(yandex.StepWaitCloudInitScript), - new(yandex.StepTeardownInstance), + &yandex.StepTeardownInstance{ + SerialLogFile: yandexConfig.SerialLogFile, + }, } // Run the steps. @@ -261,23 +255,34 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa return result, false, false, nil } -func ycSaneDefaults() yandex.Config { - return yandex.Config{ - DiskType: "network-ssd", - InstanceCores: 2, - InstanceMemory: 2, - Labels: map[string]string{ - "role": "exporter", - "target": "object-storage", +func ycSaneDefaults(c *Config, md map[string]string) yandex.Config { + yandexConfig := yandex.Config{ + CommonConfig: c.CommonConfig, + AccessConfig: c.AccessConfig, + Communicator: communicator.Config{ + Type: "ssh", + SSH: communicator.SSH{ + SSHUsername: "ubuntu", + }, }, - PlatformID: "standard-v2", - Preemptible: true, - SourceImageFamily: "ubuntu-1604-lts", - SourceImageFolderID: yandex.StandardImagesFolderID, - UseIPv4Nat: true, - Zone: "ru-central1-a", - StateTimeout: 3 * time.Minute, } + if c.SSHPrivateKeyFile != "" { + yandexConfig.Communicator.SSH.SSHPrivateKeyFile = c.SSHPrivateKeyFile + } + if yandexConfig.Metadata == nil { + yandexConfig.Metadata = md + } else { + for k, v := range md { + yandexConfig.Metadata[k] = v + } + } + + yandexConfig.SourceImageFamily = "ubuntu-1604-lts" + yandexConfig.SourceImageFolderID = yandex.StandardImagesFolderID + + yandexConfig.ServiceAccountID = c.ServiceAccountID + + return yandexConfig } func formUrls(paths []string) []string { diff --git a/post-processor/yandex-export/post-processor.hcl2spec.go b/post-processor/yandex-export/post-processor.hcl2spec.go index 8bbaf48c3..6d575bd05 100644 --- a/post-processor/yandex-export/post-processor.hcl2spec.go +++ b/post-processor/yandex-export/post-processor.hcl2spec.go @@ -21,14 +21,30 @@ type FlatConfig struct { ServiceAccountKeyFile *string `mapstructure:"service_account_key_file" required:"false" cty:"service_account_key_file" hcl:"service_account_key_file"` Token *string `mapstructure:"token" required:"true" cty:"token" hcl:"token"` MaxRetries *int `mapstructure:"max_retries" cty:"max_retries" hcl:"max_retries"` - Paths []string `mapstructure:"paths" required:"true" cty:"paths" hcl:"paths"` - FolderID *string `mapstructure:"folder_id" required:"true" cty:"folder_id" hcl:"folder_id"` - ServiceAccountID *string `mapstructure:"service_account_id" required:"true" cty:"service_account_id" hcl:"service_account_id"` - DiskSizeGb *int `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"` - DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type" hcl:"disk_type"` + SerialLogFile *string `mapstructure:"serial_log_file" required:"false" cty:"serial_log_file" hcl:"serial_log_file"` + StateTimeout *string `mapstructure:"state_timeout" required:"false" cty:"state_timeout" hcl:"state_timeout"` + InstanceCores *int `mapstructure:"instance_cores" required:"false" cty:"instance_cores" hcl:"instance_cores"` + InstanceGpus *int `mapstructure:"instance_gpus" required:"false" cty:"instance_gpus" hcl:"instance_gpus"` + InstanceMemory *int `mapstructure:"instance_mem_gb" required:"false" cty:"instance_mem_gb" hcl:"instance_mem_gb"` + InstanceName *string `mapstructure:"instance_name" required:"false" cty:"instance_name" hcl:"instance_name"` PlatformID *string `mapstructure:"platform_id" required:"false" cty:"platform_id" hcl:"platform_id"` + Labels map[string]string `mapstructure:"labels" required:"false" cty:"labels" hcl:"labels"` + Metadata map[string]string `mapstructure:"metadata" required:"false" cty:"metadata" hcl:"metadata"` + MetadataFromFile map[string]string `mapstructure:"metadata_from_file" cty:"metadata_from_file" hcl:"metadata_from_file"` + Preemptible *bool `mapstructure:"preemptible" cty:"preemptible" hcl:"preemptible"` + DiskName *string `mapstructure:"disk_name" required:"false" cty:"disk_name" hcl:"disk_name"` + DiskSizeGb *int `mapstructure:"disk_size_gb" required:"false" cty:"disk_size_gb" hcl:"disk_size_gb"` + DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type" hcl:"disk_type"` + DiskLabels map[string]string `mapstructure:"disk_labels" required:"false" cty:"disk_labels" hcl:"disk_labels"` SubnetID *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"` Zone *string `mapstructure:"zone" required:"false" cty:"zone" hcl:"zone"` + UseIPv4Nat *bool `mapstructure:"use_ipv4_nat" required:"false" cty:"use_ipv4_nat" hcl:"use_ipv4_nat"` + UseIPv6 *bool `mapstructure:"use_ipv6" required:"false" cty:"use_ipv6" hcl:"use_ipv6"` + UseInternalIP *bool `mapstructure:"use_internal_ip" required:"false" cty:"use_internal_ip" hcl:"use_internal_ip"` + FolderID *string `mapstructure:"folder_id" required:"true" cty:"folder_id" hcl:"folder_id"` + ServiceAccountID *string `mapstructure:"service_account_id" required:"true" cty:"service_account_id" hcl:"service_account_id"` + Paths []string `mapstructure:"paths" required:"true" cty:"paths" hcl:"paths"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" required:"false" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"` } // FlatMapstructure returns a new FlatConfig. @@ -55,14 +71,30 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "service_account_key_file": &hcldec.AttrSpec{Name: "service_account_key_file", Type: cty.String, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false}, - "paths": &hcldec.AttrSpec{Name: "paths", Type: cty.List(cty.String), Required: false}, - "folder_id": &hcldec.AttrSpec{Name: "folder_id", Type: cty.String, Required: false}, - "service_account_id": &hcldec.AttrSpec{Name: "service_account_id", Type: cty.String, Required: false}, - "disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false}, - "disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false}, + "serial_log_file": &hcldec.AttrSpec{Name: "serial_log_file", Type: cty.String, Required: false}, + "state_timeout": &hcldec.AttrSpec{Name: "state_timeout", Type: cty.String, Required: false}, + "instance_cores": &hcldec.AttrSpec{Name: "instance_cores", Type: cty.Number, Required: false}, + "instance_gpus": &hcldec.AttrSpec{Name: "instance_gpus", Type: cty.Number, Required: false}, + "instance_mem_gb": &hcldec.AttrSpec{Name: "instance_mem_gb", Type: cty.Number, Required: false}, + "instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false}, "platform_id": &hcldec.AttrSpec{Name: "platform_id", Type: cty.String, Required: false}, + "labels": &hcldec.AttrSpec{Name: "labels", Type: cty.Map(cty.String), Required: false}, + "metadata": &hcldec.AttrSpec{Name: "metadata", Type: cty.Map(cty.String), Required: false}, + "metadata_from_file": &hcldec.AttrSpec{Name: "metadata_from_file", Type: cty.Map(cty.String), Required: false}, + "preemptible": &hcldec.AttrSpec{Name: "preemptible", Type: cty.Bool, Required: false}, + "disk_name": &hcldec.AttrSpec{Name: "disk_name", Type: cty.String, Required: false}, + "disk_size_gb": &hcldec.AttrSpec{Name: "disk_size_gb", Type: cty.Number, Required: false}, + "disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false}, + "disk_labels": &hcldec.AttrSpec{Name: "disk_labels", Type: cty.Map(cty.String), Required: false}, "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, "zone": &hcldec.AttrSpec{Name: "zone", Type: cty.String, Required: false}, + "use_ipv4_nat": &hcldec.AttrSpec{Name: "use_ipv4_nat", Type: cty.Bool, Required: false}, + "use_ipv6": &hcldec.AttrSpec{Name: "use_ipv6", Type: cty.Bool, Required: false}, + "use_internal_ip": &hcldec.AttrSpec{Name: "use_internal_ip", Type: cty.Bool, Required: false}, + "folder_id": &hcldec.AttrSpec{Name: "folder_id", Type: cty.String, Required: false}, + "service_account_id": &hcldec.AttrSpec{Name: "service_account_id", Type: cty.String, Required: false}, + "paths": &hcldec.AttrSpec{Name: "paths", Type: cty.List(cty.String), Required: false}, + "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, } return s } diff --git a/post-processor/yandex-export/post-processor_test.go b/post-processor/yandex-export/post-processor_test.go index ac5e850a0..5befec952 100644 --- a/post-processor/yandex-export/post-processor_test.go +++ b/post-processor/yandex-export/post-processor_test.go @@ -31,6 +31,14 @@ func TestPostProcessor_Configure(t *testing.T) { Token: "", ServiceAccountKeyFile: "", }, + ExchangeConfig: ExchangeConfig{ + ServiceAccountID: "some-srv-acc-id", + }, + CommonConfig: yandex.CommonConfig{ + CloudConfig: yandex.CloudConfig{ + FolderID: "some-folder-id", + }, + }, }, }, wantErr: false, @@ -43,6 +51,14 @@ func TestPostProcessor_Configure(t *testing.T) { Token: "some-value", ServiceAccountKeyFile: "path/not-exist.file", }, + ExchangeConfig: ExchangeConfig{ + ServiceAccountID: "some-srv-acc-id", + }, + CommonConfig: yandex.CommonConfig{ + CloudConfig: yandex.CloudConfig{ + FolderID: "some-folder-id", + }, + }, }, }, wantErr: true, @@ -55,10 +71,56 @@ func TestPostProcessor_Configure(t *testing.T) { Token: "", ServiceAccountKeyFile: "testdata/fake-sa-key.json", }, + ExchangeConfig: ExchangeConfig{ + ServiceAccountID: "some-srv-acc-id", + }, + CommonConfig: yandex.CommonConfig{ + CloudConfig: yandex.CloudConfig{ + FolderID: "some-folder-id", + }, + }, }, }, wantErr: false, }, + { + name: "service_account_id required", + fields: fields{ + config: Config{ + AccessConfig: yandex.AccessConfig{ + Token: "some token", + }, + ExchangeConfig: ExchangeConfig{ + ServiceAccountID: "", + }, + CommonConfig: yandex.CommonConfig{ + CloudConfig: yandex.CloudConfig{ + FolderID: "some-folder-id", + }, + }, + }, + }, + wantErr: true, + }, + { + name: "folderID required", + fields: fields{ + config: Config{ + AccessConfig: yandex.AccessConfig{ + Token: "some token", + }, + ExchangeConfig: ExchangeConfig{ + ServiceAccountID: "some-srv-acc-id", + }, + CommonConfig: yandex.CommonConfig{ + CloudConfig: yandex.CloudConfig{ + FolderID: "", + }, + }, + }, + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/post-processor/yandex-import/post-processor.go b/post-processor/yandex-import/post-processor.go index 58445c1d4..9d6b796f4 100644 --- a/post-processor/yandex-import/post-processor.go +++ b/post-processor/yandex-import/post-processor.go @@ -6,7 +6,6 @@ package yandeximport import ( "context" "fmt" - "os" "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/file" @@ -22,14 +21,11 @@ import ( ) type Config struct { - common.PackerConfig `mapstructure:",squash"` - yandex.AccessConfig `mapstructure:",squash"` - - // The folder ID that will be used to store imported Image. - FolderID string `mapstructure:"folder_id" required:"true"` - // Service Account ID with proper permission to use Storage service - // for operations 'upload' and 'delete' object to `bucket`. - ServiceAccountID string `mapstructure:"service_account_id" required:"true"` + common.PackerConfig `mapstructure:",squash"` + yandex.AccessConfig `mapstructure:",squash"` + yandex.CloudConfig `mapstructure:",squash"` + yandexexport.ExchangeConfig `mapstructure:",squash"` + yandex.ImageConfig `mapstructure:",squash"` // The name of the bucket where the qcow2 file will be uploaded to for import. // This bucket must exist when the post-processor is run. @@ -46,16 +42,6 @@ type Config struct { // leave it in the bucket, `false` to remove it. Default is `false`. SkipClean bool `mapstructure:"skip_clean" required:"false"` - // The name of the image, which contains 1-63 characters and only - // supports lowercase English characters, numbers and hyphen. - ImageName string `mapstructure:"image_name" required:"false"` - // The description of the image. - ImageDescription string `mapstructure:"image_description" required:"false"` - // The family name of the imported image. - ImageFamily string `mapstructure:"image_family" required:"false"` - // Key/value pair labels to apply to the imported image. - ImageLabels map[string]string `mapstructure:"image_labels" required:"false"` - ctx interpolate.Context } @@ -84,10 +70,9 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { var errs *packersdk.MultiError errs = packersdk.MultiErrorAppend(errs, p.config.AccessConfig.Prepare(&p.config.ctx)...) - - if p.config.FolderID == "" { - p.config.FolderID = os.Getenv("YC_FOLDER_ID") - } + errs = p.config.CloudConfig.Prepare(errs) + errs = p.config.ImageConfig.Prepare(errs) + errs = p.config.ExchangeConfig.Prepare(errs) // Set defaults if p.config.ObjectName == "" { @@ -100,20 +85,6 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { errs, fmt.Errorf("error parsing object_name template: %s", err)) } - // TODO: make common code to check and prepare Yandex.Cloud auth configuration data - - templates := map[string]*string{ - "object_name": &p.config.ObjectName, - "folder_id": &p.config.FolderID, - } - - for key, ptr := range templates { - if *ptr == "" { - errs = packersdk.MultiErrorAppend( - errs, fmt.Errorf("%s must be set", key)) - } - } - if len(errs.Errors) > 0 { return errs } @@ -199,7 +170,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa return nil, false, false, err } - ycImage, err := createYCImage(ctx, client, ui, p.config.FolderID, imageSrc, p.config.ImageName, p.config.ImageDescription, p.config.ImageFamily, p.config.ImageLabels) + ycImage, err := createYCImage(ctx, client, ui, imageSrc, &p.config) if err != nil { return nil, false, false, err } diff --git a/post-processor/yandex-import/post-processor.hcl2spec.go b/post-processor/yandex-import/post-processor.hcl2spec.go index 1de14d668..1e50916d1 100644 --- a/post-processor/yandex-import/post-processor.hcl2spec.go +++ b/post-processor/yandex-import/post-processor.hcl2spec.go @@ -23,13 +23,15 @@ type FlatConfig struct { MaxRetries *int `mapstructure:"max_retries" cty:"max_retries" hcl:"max_retries"` FolderID *string `mapstructure:"folder_id" required:"true" cty:"folder_id" hcl:"folder_id"` ServiceAccountID *string `mapstructure:"service_account_id" required:"true" cty:"service_account_id" hcl:"service_account_id"` - Bucket *string `mapstructure:"bucket" required:"false" cty:"bucket" hcl:"bucket"` - ObjectName *string `mapstructure:"object_name" required:"false" cty:"object_name" hcl:"object_name"` - SkipClean *bool `mapstructure:"skip_clean" required:"false" cty:"skip_clean" hcl:"skip_clean"` ImageName *string `mapstructure:"image_name" required:"false" cty:"image_name" hcl:"image_name"` ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description" hcl:"image_description"` ImageFamily *string `mapstructure:"image_family" required:"false" cty:"image_family" hcl:"image_family"` ImageLabels map[string]string `mapstructure:"image_labels" required:"false" cty:"image_labels" hcl:"image_labels"` + ImageMinDiskSizeGb *int `mapstructure:"image_min_disk_size_gb" required:"false" cty:"image_min_disk_size_gb" hcl:"image_min_disk_size_gb"` + ImageProductIDs []string `mapstructure:"image_product_ids" required:"false" cty:"image_product_ids" hcl:"image_product_ids"` + Bucket *string `mapstructure:"bucket" required:"false" cty:"bucket" hcl:"bucket"` + ObjectName *string `mapstructure:"object_name" required:"false" cty:"object_name" hcl:"object_name"` + SkipClean *bool `mapstructure:"skip_clean" required:"false" cty:"skip_clean" hcl:"skip_clean"` } // FlatMapstructure returns a new FlatConfig. @@ -58,13 +60,15 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false}, "folder_id": &hcldec.AttrSpec{Name: "folder_id", Type: cty.String, Required: false}, "service_account_id": &hcldec.AttrSpec{Name: "service_account_id", Type: cty.String, Required: false}, - "bucket": &hcldec.AttrSpec{Name: "bucket", Type: cty.String, Required: false}, - "object_name": &hcldec.AttrSpec{Name: "object_name", Type: cty.String, Required: false}, - "skip_clean": &hcldec.AttrSpec{Name: "skip_clean", Type: cty.Bool, Required: false}, "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, "image_family": &hcldec.AttrSpec{Name: "image_family", Type: cty.String, Required: false}, "image_labels": &hcldec.AttrSpec{Name: "image_labels", Type: cty.Map(cty.String), Required: false}, + "image_min_disk_size_gb": &hcldec.AttrSpec{Name: "image_min_disk_size_gb", Type: cty.Number, Required: false}, + "image_product_ids": &hcldec.AttrSpec{Name: "image_product_ids", Type: cty.List(cty.String), Required: false}, + "bucket": &hcldec.AttrSpec{Name: "bucket", Type: cty.String, Required: false}, + "object_name": &hcldec.AttrSpec{Name: "object_name", Type: cty.String, Required: false}, + "skip_clean": &hcldec.AttrSpec{Name: "skip_clean", Type: cty.Bool, Required: false}, } return s } diff --git a/post-processor/yandex-import/utils.go b/post-processor/yandex-import/utils.go index d9a10844b..40eb874de 100644 --- a/post-processor/yandex-import/utils.go +++ b/post-processor/yandex-import/utils.go @@ -66,13 +66,15 @@ func uploadToBucket(s3conn *s3.S3, ui packersdk.Ui, artifact packersdk.Artifact, }, nil } -func createYCImage(ctx context.Context, driver yandex.Driver, ui packersdk.Ui, folderID string, imageSrc cloudImageSource, imageName string, imageDescription string, imageFamily string, imageLabels map[string]string) (*compute.Image, error) { +func createYCImage(ctx context.Context, driver yandex.Driver, ui packersdk.Ui, imageSrc cloudImageSource, c *Config) (*compute.Image, error) { req := &compute.CreateImageRequest{ - FolderId: folderID, - Name: imageName, - Description: imageDescription, - Labels: imageLabels, - Family: imageFamily, + FolderId: c.CloudConfig.FolderID, + Name: c.ImageConfig.ImageName, + Description: c.ImageConfig.ImageDescription, + Labels: c.ImageConfig.ImageLabels, + Family: c.ImageConfig.ImageFamily, + MinDiskSize: int64(c.ImageMinDiskSizeGb), + ProductIds: c.ImageProductIDs, } // switch on cloudImageSource type: cloud image id or storage URL @@ -91,7 +93,7 @@ func createYCImage(ctx context.Context, driver yandex.Driver, ui packersdk.Ui, f ui.Say(fmt.Sprintf("Source of Image creation: %s", imageSrc.Description())) - ui.Say(fmt.Sprintf("Creating Yandex Compute Image %v within operation %#v", imageName, op.Id())) + ui.Say(fmt.Sprintf("Creating Yandex Compute Image %v within operation %#v", c.ImageName, op.Id())) ui.Say("Waiting for Yandex Compute Image creation operation to complete...") err = op.Wait(ctx) diff --git a/website/pages/docs/builders/yandex.mdx b/website/pages/docs/builders/yandex.mdx index 12243997f..440ca5ad6 100644 --- a/website/pages/docs/builders/yandex.mdx +++ b/website/pages/docs/builders/yandex.mdx @@ -73,16 +73,48 @@ can also be supplied to override the typical auto-generated key: ### Required: +#### Access + @include 'builder/yandex/AccessConfig-required.mdx' +#### Builder + @include 'builder/yandex/Config-required.mdx' +#### Common + +@include 'builder/yandex/CloudConfig-required.mdx' + ### Optional: +#### Access + @include 'builder/yandex/AccessConfig-not-required.mdx' +#### Builder + @include 'builder/yandex/Config-not-required.mdx' +#### Common + +@include 'builder/yandex/CommonConfig-not-required.mdx' + +#### Instance + +@include 'builder/yandex/InstanceConfig-not-required.mdx' + +#### Disk + +@include 'builder/yandex/DiskConfig-not-required.mdx' + +#### Image + +@include 'builder/yandex/ImageConfig-not-required.mdx' + +#### Network + +@include 'builder/yandex/NetworkConfig-not-required.mdx' + ## Build template data In configuration directives the following variables are available: diff --git a/website/pages/docs/post-processors/yandex-export.mdx b/website/pages/docs/post-processors/yandex-export.mdx index 668535275..9d8c65f3d 100644 --- a/website/pages/docs/post-processors/yandex-export.mdx +++ b/website/pages/docs/post-processors/yandex-export.mdx @@ -33,16 +33,46 @@ image. ### Required: +#### Access + @include 'builder/yandex/AccessConfig-required.mdx' +#### Export + @include 'post-processor/yandex-export/Config-required.mdx' +#### Common + +@include 'builder/yandex/CloudConfig-required.mdx' + +@include 'post-processor/yandex-export/ExchangeConfig-required.mdx' + ### Optional: +#### Access + @include 'builder/yandex/AccessConfig-not-required.mdx' +#### Export + @include 'post-processor/yandex-export/Config-not-required.mdx' +#### Common + +@include 'builder/yandex/CommonConfig-not-required.mdx' + +#### Instance + +@include 'builder/yandex/InstanceConfig-not-required.mdx' + +#### Disk + +@include 'builder/yandex/DiskConfig-not-required.mdx' + +#### Network + +@include 'builder/yandex/NetworkConfig-not-required.mdx' + ## Basic Example The following example builds a Compute image in the folder with id `b1g8jvfcgmitdrslcn86`, with an diff --git a/website/pages/docs/post-processors/yandex-import.mdx b/website/pages/docs/post-processors/yandex-import.mdx index 29b4e47bc..33d34c6f5 100644 --- a/website/pages/docs/post-processors/yandex-import.mdx +++ b/website/pages/docs/post-processors/yandex-import.mdx @@ -25,16 +25,30 @@ file. ### Required: +#### Access + @include 'builder/yandex/AccessConfig-required.mdx' -@include 'post-processor/yandex-import/Config-required.mdx' +#### Common + +@include 'builder/yandex/CloudConfig-required.mdx' + +@include 'post-processor/yandex-export/ExchangeConfig-required.mdx' ### Optional: +#### Access + @include 'builder/yandex/AccessConfig-not-required.mdx' +#### Import + @include 'post-processor/yandex-import/Config-not-required.mdx' +#### Image + +@include 'builder/yandex/ImageConfig-not-required.mdx' + ## Basic Example TBD diff --git a/website/pages/partials/builder/yandex/CloudConfig-required.mdx b/website/pages/partials/builder/yandex/CloudConfig-required.mdx new file mode 100644 index 000000000..75f93b60c --- /dev/null +++ b/website/pages/partials/builder/yandex/CloudConfig-required.mdx @@ -0,0 +1,6 @@ + + +- `folder_id` (string) - 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'. diff --git a/website/pages/partials/builder/yandex/CommonConfig-not-required.mdx b/website/pages/partials/builder/yandex/CommonConfig-not-required.mdx new file mode 100644 index 000000000..65b117798 --- /dev/null +++ b/website/pages/partials/builder/yandex/CommonConfig-not-required.mdx @@ -0,0 +1,6 @@ + + +- `serial_log_file` (string) - File path to save serial port output of the launched instance. + +- `state_timeout` (duration string | ex: "1h5m2s") - The time to wait for instance state changes. + Defaults to `5m`. diff --git a/website/pages/partials/builder/yandex/Config-not-required.mdx b/website/pages/partials/builder/yandex/Config-not-required.mdx index 806a1d3ee..0a5bbcbed 100644 --- a/website/pages/partials/builder/yandex/Config-not-required.mdx +++ b/website/pages/partials/builder/yandex/Config-not-required.mdx @@ -2,50 +2,6 @@ - `service_account_id` (string) - Service account identifier to assign to instance. -- `disk_name` (string) - The name of the disk, if unset the instance name - will be used. - -- `disk_size_gb` (int) - The size of the disk in GB. This defaults to `10`, which is 10GB. - -- `disk_type` (string) - Specify disk type for the launched instance. Defaults to `network-hdd`. - -- `disk_labels` (map[string]string) - Key/value pair labels to apply to the disk. - -- `image_description` (string) - The description of the resulting image. - -- `image_family` (string) - The family name of the resulting image. - -- `image_labels` (map[string]string) - Key/value pair labels to apply to the created image. - -- `image_min_disk_size_gb` (int) - Minimum size of the disk that will be created from built image, specified in gigabytes. - Should be more or equal to `disk_size_gb`. - -- `image_name` (string) - The unique name of the resulting image. Defaults to - `packer-{{timestamp}}`. - -- `image_product_ids` ([]string) - License IDs that indicate which licenses are attached to resulting image. - -- `instance_cores` (int) - The number of cores available to the instance. - -- `instance_gpus` (int) - The number of GPU available to the instance. - -- `instance_mem_gb` (int) - The amount of memory available to the instance, specified in gigabytes. - -- `instance_name` (string) - The name assigned to the instance. - -- `labels` (map[string]string) - Key/value pair labels to apply to the launched instance. - -- `platform_id` (string) - Identifier of the hardware platform configuration for the instance. This defaults to `standard-v1`. - -- `metadata` (map[string]string) - Metadata applied to the launched instance. - -- `metadata_from_file` (map[string]string) - Metadata applied to the launched instance. - The values in this map are the paths to the content files for the corresponding metadata keys. - -- `preemptible` (bool) - Launch a preemptible instance. This defaults to `false`. - -- `serial_log_file` (string) - File path to save serial port output of the launched instance. - - `source_image_folder_id` (string) - The ID of the folder containing the source image. - `source_image_id` (string) - The source image ID to use to create the new image from. @@ -53,25 +9,5 @@ - `source_image_name` (string) - The source image name to use to create the new image from. Name will be looked up in `source_image_folder_id`. -- `subnet_id` (string) - 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. - - `target_image_folder_id` (string) - The ID of the folder to save built image in. This defaults to value of 'folder_id'. - -- `use_ipv4_nat` (bool) - If set to true, then launched instance will have external internet - access. - -- `use_ipv6` (bool) - 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. - -- `use_internal_ip` (bool) - If true, use the instance's internal IP address - instead of its external IP during building. - -- `zone` (string) - The name of the zone to launch the instance. This defaults to `ru-central1-a`. - -- `state_timeout` (duration string | ex: "1h5m2s") - The time to wait for instance state changes. - Defaults to `5m`. diff --git a/website/pages/partials/builder/yandex/Config-required.mdx b/website/pages/partials/builder/yandex/Config-required.mdx index 995400618..8b0859ccc 100644 --- a/website/pages/partials/builder/yandex/Config-required.mdx +++ b/website/pages/partials/builder/yandex/Config-required.mdx @@ -1,10 +1,5 @@ -- `folder_id` (string) - 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'. - - `source_image_family` (string) - The source image family to create the new image 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`. diff --git a/website/pages/partials/builder/yandex/DiskConfig-not-required.mdx b/website/pages/partials/builder/yandex/DiskConfig-not-required.mdx new file mode 100644 index 000000000..0b3d2c910 --- /dev/null +++ b/website/pages/partials/builder/yandex/DiskConfig-not-required.mdx @@ -0,0 +1,10 @@ + + +- `disk_name` (string) - The name of the disk, if unset the instance name + will be used. + +- `disk_size_gb` (int) - The size of the disk in GB. This defaults to 10/100GB. + +- `disk_type` (string) - Specify disk type for the launched instance. Defaults to `network-ssd`. + +- `disk_labels` (map[string]string) - Key/value pair labels to apply to the disk. diff --git a/website/pages/partials/builder/yandex/ImageConfig-not-required.mdx b/website/pages/partials/builder/yandex/ImageConfig-not-required.mdx new file mode 100644 index 000000000..f4347ecb2 --- /dev/null +++ b/website/pages/partials/builder/yandex/ImageConfig-not-required.mdx @@ -0,0 +1,16 @@ + + +- `image_name` (string) - The name of the resulting image, which contains 1-63 characters and only + supports lowercase English characters, numbers and hyphen. Defaults to + `packer-{{timestamp}}`. + +- `image_description` (string) - The description of the image. + +- `image_family` (string) - The family name of the image. + +- `image_labels` (map[string]string) - Key/value pair labels to apply to the image. + +- `image_min_disk_size_gb` (int) - Minimum size of the disk that will be created from built image, specified in gigabytes. + Should be more or equal to `disk_size_gb`. + +- `image_product_ids` ([]string) - License IDs that indicate which licenses are attached to resulting image. diff --git a/website/pages/partials/builder/yandex/InstanceConfig-not-required.mdx b/website/pages/partials/builder/yandex/InstanceConfig-not-required.mdx new file mode 100644 index 000000000..816865a0e --- /dev/null +++ b/website/pages/partials/builder/yandex/InstanceConfig-not-required.mdx @@ -0,0 +1,20 @@ + + +- `instance_cores` (int) - The number of cores available to the instance. + +- `instance_gpus` (int) - The number of GPU available to the instance. + +- `instance_mem_gb` (int) - The amount of memory available to the instance, specified in gigabytes. + +- `instance_name` (string) - The name assigned to the instance. + +- `platform_id` (string) - Identifier of the hardware platform configuration for the instance. This defaults to `standard-v2`. + +- `labels` (map[string]string) - Key/value pair labels to apply to the launched instance. + +- `metadata` (map[string]string) - Metadata applied to the launched instance. + +- `metadata_from_file` (map[string]string) - Metadata applied to the launched instance. + The values in this map are the paths to the content files for the corresponding metadata keys. + +- `preemptible` (bool) - Launch a preemptible instance. This defaults to `false`. diff --git a/website/pages/partials/builder/yandex/NetworkConfig-not-required.mdx b/website/pages/partials/builder/yandex/NetworkConfig-not-required.mdx new file mode 100644 index 000000000..2e61ed622 --- /dev/null +++ b/website/pages/partials/builder/yandex/NetworkConfig-not-required.mdx @@ -0,0 +1,18 @@ + + +- `subnet_id` (string) - 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. + +- `zone` (string) - The name of the zone to launch the instance. This defaults to `ru-central1-a`. + +- `use_ipv4_nat` (bool) - If set to true, then launched instance will have external internet + access. + +- `use_ipv6` (bool) - 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. + +- `use_internal_ip` (bool) - If true, use the instance's internal IP address + instead of its external IP during building. diff --git a/website/pages/partials/post-processor/yandex-export/Config-not-required.mdx b/website/pages/partials/post-processor/yandex-export/Config-not-required.mdx index 531bc012b..192378239 100644 --- a/website/pages/partials/post-processor/yandex-export/Config-not-required.mdx +++ b/website/pages/partials/post-processor/yandex-export/Config-not-required.mdx @@ -1,13 +1,5 @@ -- `disk_size` (int) - The size of the disk in GB. This defaults to `100`, which is 100GB. - -- `disk_type` (string) - Specify disk type for the launched instance. Defaults to `network-ssd`. - -- `platform_id` (string) - Identifier of the hardware platform configuration for the instance. This defaults to `standard-v2`. - -- `subnet_id` (string) - 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. - -- `zone` (string) - The name of the zone to launch the instance. This defaults to `ru-central1-a`. +- `ssh_private_key_file` (string) - Path to a PEM encoded private key file to use to authenticate with SSH. + The `~` can be used in path and will be expanded to the home directory + of current user. Login for attach: `ubuntu` diff --git a/website/pages/partials/post-processor/yandex-export/Config-required.mdx b/website/pages/partials/post-processor/yandex-export/Config-required.mdx index 170ec0adb..a051f192c 100644 --- a/website/pages/partials/post-processor/yandex-export/Config-required.mdx +++ b/website/pages/partials/post-processor/yandex-export/Config-required.mdx @@ -5,9 +5,3 @@ Also this param support [build](/docs/templates/engine) template function. Check available template data for [Yandex](/docs/builders/yandex#build-template-data) builder. Paths to Yandex Object Storage where exported image will be uploaded. - -- `folder_id` (string) - The folder ID that will be used to launch a temporary instance. - Alternatively you may set value by environment variable `YC_FOLDER_ID`. - -- `service_account_id` (string) - Service Account ID with proper permission to modify an instance, create and attach disk and - make upload to specific Yandex Object Storage paths. diff --git a/website/pages/partials/post-processor/yandex-export/ExchangeConfig-required.mdx b/website/pages/partials/post-processor/yandex-export/ExchangeConfig-required.mdx new file mode 100644 index 000000000..d80909b29 --- /dev/null +++ b/website/pages/partials/post-processor/yandex-export/ExchangeConfig-required.mdx @@ -0,0 +1,4 @@ + + +- `service_account_id` (string) - Service Account ID with proper permission to modify an instance, create and attach disk and + make upload to specific Yandex Object Storage paths. diff --git a/website/pages/partials/post-processor/yandex-import/Config-not-required.mdx b/website/pages/partials/post-processor/yandex-import/Config-not-required.mdx index 08c735a5c..636cb9f04 100644 --- a/website/pages/partials/post-processor/yandex-import/Config-not-required.mdx +++ b/website/pages/partials/post-processor/yandex-import/Config-not-required.mdx @@ -13,12 +13,3 @@ - `skip_clean` (bool) - Whether skip removing the qcow2 file uploaded to Storage after the import process has completed. Possible values are: `true` to leave it in the bucket, `false` to remove it. Default is `false`. - -- `image_name` (string) - The name of the image, which contains 1-63 characters and only - supports lowercase English characters, numbers and hyphen. - -- `image_description` (string) - The description of the image. - -- `image_family` (string) - The family name of the imported image. - -- `image_labels` (map[string]string) - Key/value pair labels to apply to the imported image. diff --git a/website/pages/partials/post-processor/yandex-import/Config-required.mdx b/website/pages/partials/post-processor/yandex-import/Config-required.mdx deleted file mode 100644 index 8b665055b..000000000 --- a/website/pages/partials/post-processor/yandex-import/Config-required.mdx +++ /dev/null @@ -1,6 +0,0 @@ - - -- `folder_id` (string) - The folder ID that will be used to store imported Image. - -- `service_account_id` (string) - Service Account ID with proper permission to use Storage service - for operations 'upload' and 'delete' object to `bucket`.