Merge pull request #10317 from Direnol/yandex/common-configs
reuse common configs
This commit is contained in:
commit
e117117ea5
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
}))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<!-- Code generated from the comments of the CloudConfig struct in builder/yandex/common_config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `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'.
|
|
@ -0,0 +1,6 @@
|
|||
<!-- Code generated from the comments of the CommonConfig struct in builder/yandex/common_config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `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`.
|
|
@ -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`.
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
<!-- Code generated from the comments of the Config struct in builder/yandex/config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `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`.
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<!-- Code generated from the comments of the DiskConfig struct in builder/yandex/common_config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `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.
|
|
@ -0,0 +1,16 @@
|
|||
<!-- Code generated from the comments of the ImageConfig struct in builder/yandex/common_config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `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.
|
|
@ -0,0 +1,20 @@
|
|||
<!-- Code generated from the comments of the InstanceConfig struct in builder/yandex/common_config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `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`.
|
|
@ -0,0 +1,18 @@
|
|||
<!-- Code generated from the comments of the NetworkConfig struct in builder/yandex/common_config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `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.
|
|
@ -1,13 +1,5 @@
|
|||
<!-- Code generated from the comments of the Config struct in post-processor/yandex-export/post-processor.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `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`
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<!-- Code generated from the comments of the ExchangeConfig struct in post-processor/yandex-export/config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `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.
|
|
@ -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.
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
<!-- Code generated from the comments of the Config struct in post-processor/yandex-import/post-processor.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `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`.
|
Loading…
Reference in New Issue