reuse common configs

This commit is contained in:
Roman Mingazeev
2020-12-07 18:13:06 +03:00
parent adb4a14471
commit 478fda12ba
32 changed files with 713 additions and 464 deletions
+8 -1
View File
@@ -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
}
+3 -1
View File
@@ -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,
},
+232
View File
@@ -0,0 +1,232 @@
//go:generate struct-markdown
package yandex
import (
"errors"
"fmt"
"os"
"regexp"
"github.com/hashicorp/packer/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"`
InstanceConfig `mapstructure:",squash"`
DiskConfig `mapstructure:",squash"`
NetworkConfig `mapstructure:",squash"`
CloudConfig `mapstructure:",squash"`
}
func (c *CommonConfig) Prepare(errs *packer.MultiError) *packer.MultiError {
if c.SerialLogFile != "" {
if _, err := os.Stat(c.SerialLogFile); os.IsExist(err) {
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Serial log file %s already exist", c.SerialLogFile))
}
}
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 *packer.MultiError) *packer.MultiError {
if c.FolderID == "" {
c.FolderID = os.Getenv("YC_FOLDER_ID")
}
if c.FolderID == "" {
errs = packer.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 *packer.MultiError) *packer.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 *packer.MultiError) *packer.MultiError {
if c.Zone == "" {
c.Zone = defaultZone
}
// if c.UseIPv4Nat && c.UseIPv6 {
// errs = packer.MultiErrorAppend(
// errors.New("one of use_ipv4_nat or use_ipv6 key file must be specified, not both"),
// errs,
// )
// }
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 *packer.MultiError) *packer.MultiError {
if len(c.ImageFamily) > 63 {
errs = packer.MultiErrorAppend(errs,
errors.New("Invalid image family: Must not be longer than 63 characters"))
}
if c.ImageFamily != "" {
if !reImageFamily.MatchString(c.ImageFamily) {
errs = packer.MultiErrorAppend(errs,
errors.New("Invalid image family: The first character must be a "+
"lowercase letter, and all following characters must be a dash, "+
"lowercase letter, or digit, except the last character, which cannot be a dash"))
}
}
if c.ImageDescription == "" {
c.ImageDescription = "Created by Packer"
}
if c.ImageName == "" {
img, err := interpolate.Render("packer-{{timestamp}}", nil)
if err != nil {
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Unable to render default image name: %s ", err))
} else {
c.ImageName = img
}
}
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 *packer.MultiError) *packer.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 = packer.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 -161
View File
@@ -6,8 +6,6 @@ package yandex
import (
"errors"
"fmt"
"os"
"regexp"
"time"
"github.com/hashicorp/packer/packer-plugin-sdk/common"
@@ -15,72 +13,19 @@ import (
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,26 +37,9 @@ 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.
@@ -134,28 +62,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 +75,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,34 +95,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
}
+37 -37
View File
@@ -70,38 +70,38 @@ 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"`
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"`
}
@@ -178,38 +178,38 @@ 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},
"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
+17 -20
View File
@@ -76,6 +76,20 @@ func createDisk(ctx context.Context, c *Config, d Driver, sourceImage *Image) (*
}
err = op.Wait(ctx)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
resp, err2 := op.Response()
if err2 != nil {
return nil, err
}
disk, ok := resp.(*compute.Disk)
if !ok {
return nil, err
}
sdk.Compute().Disk().Delete(ctx, &compute.DeleteDiskRequest{
DiskId: disk.Id,
})
}
return nil, err
}
resp, err := op.Response()
@@ -270,6 +284,8 @@ func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag)
}
}
ui.Message(fmt.Sprint(req))
op, err := sdk.WrapOperation(sdk.Compute().Instance().Create(ctx, req))
if err != nil {
return stepHaltWithError(state, fmt.Errorf("Error create instance: %s", err))
@@ -331,7 +347,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 +411,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 -1
View File
@@ -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,
}))
+1 -1
View File
@@ -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)
+28
View File
@@ -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").(packer.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
}
+23
View File
@@ -0,0 +1,23 @@
//go:generate struct-markdown
package yandexexport
import (
"fmt"
"github.com/hashicorp/packer/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 *packer.MultiError) *packer.MultiError {
if c.ServiceAccountID == "" {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("service_account_id must be specified"))
}
return errs
}
+65 -81
View File
@@ -8,7 +8,6 @@ import (
"context"
"fmt"
"log"
"os"
"strings"
"time"
@@ -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,21 @@ 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
}
if p.config.UseIPv4Nat && 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)
}
p.config.Labels["role"] = "exporter"
p.config.Labels["target"] = "object-storage"
return nil
}
@@ -168,15 +154,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,22 +179,19 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa
},
},
}
yandexConfig := ycSaneDefaults()
// 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,
},
)
yandexConfig.InstanceConfig.InstanceName = exporterName
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
}
if p.config.PlatformID != "" {
yandexConfig.PlatformID = p.config.PlatformID
}
ui.Say(fmt.Sprintf("Validating service_account_id: '%s'...", yandexConfig.ServiceAccountID))
if err := validateServiceAccount(ctx, driver.SDK(), yandexConfig.ServiceAccountID); err != nil {
@@ -240,10 +214,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 +238,30 @@ 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",
},
PlatformID: "standard-v2",
Preemptible: true,
SourceImageFamily: "ubuntu-1604-lts",
SourceImageFolderID: yandex.StandardImagesFolderID,
UseIPv4Nat: true,
Zone: "ru-central1-a",
StateTimeout: 3 * time.Minute,
func ycSaneDefaults(c *Config, md map[string]string) yandex.Config {
yandexConfig := yandex.Config{
CommonConfig: c.CommonConfig,
AccessConfig: c.AccessConfig,
}
if c.SSHPrivateKeyFile != "" {
yandexConfig.Communicator.SSH.SSHPrivateKeyFile = c.SSHPrivateKeyFile
yandexConfig.Communicator.SSH.SSHUsername = "ubuntu"
}
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.StateTimeout = 3 * time.Minute
yandexConfig.ServiceAccountID = c.ServiceAccountID
return yandexConfig
}
func formUrls(paths []string) []string {
@@ -21,14 +21,29 @@ 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"`
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 +70,29 @@ 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},
"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,12 @@ func TestPostProcessor_Configure(t *testing.T) {
Token: "",
ServiceAccountKeyFile: "",
},
ExchangeConfig: ExchangeConfig{
ServiceAccountID: "some-srv-acc-id",
},
CommonConfig: yandex.CommonConfig{
FolderID: "some-folder-id",
},
},
},
wantErr: false,
@@ -43,6 +49,12 @@ func TestPostProcessor_Configure(t *testing.T) {
Token: "some-value",
ServiceAccountKeyFile: "path/not-exist.file",
},
ExchangeConfig: ExchangeConfig{
ServiceAccountID: "some-srv-acc-id",
},
CommonConfig: yandex.CommonConfig{
FolderID: "some-folder-id",
},
},
},
wantErr: true,
@@ -55,10 +67,50 @@ func TestPostProcessor_Configure(t *testing.T) {
Token: "",
ServiceAccountKeyFile: "testdata/fake-sa-key.json",
},
ExchangeConfig: ExchangeConfig{
ServiceAccountID: "some-srv-acc-id",
},
CommonConfig: yandex.CommonConfig{
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{
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{
FolderID: "",
},
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+10 -39
View File
@@ -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
}
@@ -83,11 +69,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
// Accumulate any errors
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 = packer.MultiErrorAppend(errs, p.config.AccessConfig.Prepare(&p.config.ctx)...)
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
}
+9 -7
View File
@@ -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 packer.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)
+32
View File
@@ -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,50 @@ 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'
#### Image
@include 'builder/yandex/ImageConfig-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,3 @@
<!-- 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.
@@ -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,8 @@
- `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.

Some files were not shown because too many files have changed in this diff Show More