yandex-import: allow set custom API endpoint (#9850)

* Separate Access Config from yandex builder Config

* make use of Access Config explicit

* Move `MaxRetries` into AccessConfig

* NewDriverYC use AccessConfig instead Config

* yandex-import PP use common Access Config

Now support set custom API Endpoint

* yandex-export PP use common Access Config

Now support set custom API Endpoint too (as yandex-import)

* fix test

* Tiny doc updates.
This commit is contained in:
GennadySpb 2020-08-31 16:29:20 +03:00 committed by GitHub
parent f578b93f7e
commit 804fefef17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 178 additions and 194 deletions

View File

@ -0,0 +1,67 @@
//go:generate struct-markdown
package yandex
import (
"errors"
"fmt"
"os"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/yandex-cloud/go-sdk/iamkey"
)
const defaultEndpoint = "api.cloud.yandex.net:443"
// AccessConfig is for common configuration related to Yandex.Cloud API access
type AccessConfig struct {
// Non standard API endpoint. Default is `api.cloud.yandex.net:443`.
Endpoint string `mapstructure:"endpoint" required:"false"`
// Path to file with Service Account key in json format. This
// is an alternative method to authenticate to Yandex.Cloud. Alternatively you may set environment variable
// `YC_SERVICE_ACCOUNT_KEY_FILE`.
ServiceAccountKeyFile string `mapstructure:"service_account_key_file" required:"false"`
// OAuth token to use to authenticate to Yandex.Cloud. Alternatively you may set
// value by environment variable `YC_TOKEN`.
Token string `mapstructure:"token" required:"true"`
// The maximum number of times an API request is being executed.
MaxRetries int `mapstructure:"max_retries"`
}
func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
if c.Endpoint == "" {
c.Endpoint = defaultEndpoint
}
// provision config by OS environment variables
if c.Token == "" {
c.Token = os.Getenv("YC_TOKEN")
}
if c.ServiceAccountKeyFile == "" {
c.ServiceAccountKeyFile = os.Getenv("YC_SERVICE_ACCOUNT_KEY_FILE")
}
if c.Token != "" && c.ServiceAccountKeyFile != "" {
errs = append(errs, errors.New("one of token or service account key file must be specified, not both"))
}
if c.Token != "" {
packer.LogSecretFilter.Set(c.Token)
}
if c.ServiceAccountKeyFile != "" {
if _, err := iamkey.ReadFromJSONFile(c.ServiceAccountKeyFile); err != nil {
errs = append(errs, fmt.Errorf("fail to read service account key file: %s", err))
}
}
if len(errs) > 0 {
return errs
}
return nil
}

View File

@ -50,7 +50,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
// Run executes a yandex Packer build and returns a packer.Artifact
// representing a Yandex.Cloud compute image.
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
driver, err := NewDriverYC(ui, &b.config)
driver, err := NewDriverYC(ui, &b.config.AccessConfig)
ctx = requestid.ContextWithClientTraceID(ctx, uuid.New().String())
if err != nil {

View File

@ -16,11 +16,8 @@ import (
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/yandex-cloud/go-sdk/iamkey"
)
const defaultEndpoint = "api.cloud.yandex.net:443"
const defaultGpuPlatformID = "gpu-standard-v1"
const defaultPlatformID = "standard-v1"
const defaultMaxRetries = 3
@ -31,23 +28,15 @@ 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"`
// Non standard api endpoint URL.
Endpoint string `mapstructure:"endpoint" required:"false"`
// The folder ID that will be used to launch instances and store images.
// Alternatively you may set value by environment variable YC_FOLDER_ID.
// 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"`
// Path to file with Service Account key in json format. This
// is an alternative method to authenticate to Yandex.Cloud. Alternatively you may set environment variable
// YC_SERVICE_ACCOUNT_KEY_FILE.
ServiceAccountKeyFile string `mapstructure:"service_account_key_file" required:"false"`
// Service account identifier to assign to instance
// Service account identifier to assign to instance.
ServiceAccountID string `mapstructure:"service_account_id" required:"false"`
// OAuth token to use to authenticate to Yandex.Cloud. Alternatively you may set
// value by environment variable YC_TOKEN.
Token string `mapstructure:"token" required:"true"`
// The name of the disk, if unset the instance name
// will be used.
DiskName string `mapstructure:"disk_name" required:"false"`
@ -59,8 +48,7 @@ type Config struct {
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.
// 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`.
@ -78,16 +66,14 @@ type Config struct {
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.
// 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"`
// The maximum number of times an API request is being executed
MaxRetries int `mapstructure:"max_retries"`
// Metadata applied to the launched instance.
Metadata map[string]string `mapstructure:"metadata" required:"false"`
// Metadata applied to the launched instance. Value are file paths.
// 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"`
@ -95,12 +81,11 @@ type Config struct {
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`
// source_image_family must be specified. Example: `ubuntu-1804-lts`.
SourceImageFamily string `mapstructure:"source_image_family" required:"true"`
// The ID of the folder containing the source image.
SourceImageFolderID string `mapstructure:"source_image_folder_id" required:"false"`
// The source image ID to use to create the new image
// from.
// The source image ID to use to create the new image from.
SourceImageID string `mapstructure:"source_image_id" required:"false"`
// The source image name to use to create the new image
// from. Name will be looked up in `source_image_folder_id`.
@ -142,8 +127,11 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
return nil, err
}
// Accumulate any errors
var errs *packer.MultiError
errs = packer.MultiErrorAppend(errs, c.AccessConfig.Prepare(&c.ctx)...)
if c.SerialLogFile != "" {
if _, err := os.Stat(c.SerialLogFile); os.IsExist(err) {
errs = packer.MultiErrorAppend(errs,
@ -236,10 +224,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
}
}
if c.Endpoint == "" {
c.Endpoint = defaultEndpoint
}
if c.Zone == "" {
c.Zone = defaultZone
}
@ -248,35 +232,10 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
c.MaxRetries = defaultMaxRetries
}
// provision config by OS environment variables
if c.Token == "" {
c.Token = os.Getenv("YC_TOKEN")
}
if c.ServiceAccountKeyFile == "" {
c.ServiceAccountKeyFile = os.Getenv("YC_SERVICE_ACCOUNT_KEY_FILE")
}
if c.FolderID == "" {
c.FolderID = os.Getenv("YC_FOLDER_ID")
}
if c.Token != "" && c.ServiceAccountKeyFile != "" {
errs = packer.MultiErrorAppend(
errs, errors.New("one of token or service account key file must be specified, not both"))
}
if c.Token != "" {
packer.LogSecretFilter.Set(c.Token)
}
if c.ServiceAccountKeyFile != "" {
if _, err := iamkey.ReadFromJSONFile(c.ServiceAccountKeyFile); err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("fail to read service account key file: %s", err))
}
}
if c.FolderID == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("a folder_id must be specified"))

View File

@ -64,10 +64,11 @@ type FlatConfig struct {
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
Endpoint *string `mapstructure:"endpoint" required:"false" cty:"endpoint" hcl:"endpoint"`
FolderID *string `mapstructure:"folder_id" required:"true" cty:"folder_id" hcl:"folder_id"`
ServiceAccountKeyFile *string `mapstructure:"service_account_key_file" required:"false" cty:"service_account_key_file" hcl:"service_account_key_file"`
ServiceAccountID *string `mapstructure:"service_account_id" required:"false" cty:"service_account_id" hcl:"service_account_id"`
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"`
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"`
@ -83,7 +84,6 @@ type FlatConfig struct {
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"`
MaxRetries *int `mapstructure:"max_retries" cty:"max_retries" hcl:"max_retries"`
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"`
@ -168,10 +168,11 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
"endpoint": &hcldec.AttrSpec{Name: "endpoint", Type: cty.String, Required: false},
"folder_id": &hcldec.AttrSpec{Name: "folder_id", Type: cty.String, Required: false},
"service_account_key_file": &hcldec.AttrSpec{Name: "service_account_key_file", Type: cty.String, Required: false},
"service_account_id": &hcldec.AttrSpec{Name: "service_account_id", 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},
@ -187,7 +188,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"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},
"max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, 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},

View File

@ -33,27 +33,27 @@ type driverYC struct {
ui packer.Ui
}
func NewDriverYC(ui packer.Ui, config *Config) (Driver, error) {
func NewDriverYC(ui packer.Ui, ac *AccessConfig) (Driver, error) {
log.Printf("[INFO] Initialize Yandex.Cloud client...")
sdkConfig := ycsdk.Config{}
if config.Endpoint != "" {
sdkConfig.Endpoint = config.Endpoint
if ac.Endpoint != "" {
sdkConfig.Endpoint = ac.Endpoint
}
switch {
case config.Token == "" && config.ServiceAccountKeyFile == "":
case ac.Token == "" && ac.ServiceAccountKeyFile == "":
log.Printf("[INFO] Use Instance Service Account for authentication")
sdkConfig.Credentials = ycsdk.InstanceServiceAccount()
case config.Token != "":
case ac.Token != "":
log.Printf("[INFO] Use OAuth token for authentication")
sdkConfig.Credentials = ycsdk.OAuthToken(config.Token)
sdkConfig.Credentials = ycsdk.OAuthToken(ac.Token)
case config.ServiceAccountKeyFile != "":
log.Printf("[INFO] Use Service Account key file %q for authentication", config.ServiceAccountKeyFile)
key, err := iamkey.ReadFromJSONFile(config.ServiceAccountKeyFile)
case ac.ServiceAccountKeyFile != "":
log.Printf("[INFO] Use Service Account key file %q for authentication", ac.ServiceAccountKeyFile)
key, err := iamkey.ReadFromJSONFile(ac.ServiceAccountKeyFile)
if err != nil {
return nil, err
}
@ -69,7 +69,7 @@ func NewDriverYC(ui packer.Ui, config *Config) (Driver, error) {
requestIDInterceptor := requestid.Interceptor()
retryInterceptor := retry.Interceptor(
retry.WithMax(config.MaxRetries),
retry.WithMax(ac.MaxRetries),
retry.WithCodes(codes.Unavailable),
retry.WithAttemptHeader(true),
retry.WithBackoff(retry.BackoffExponentialWithJitter(defaultExponentialBackoffBase, defaultExponentialBackoffCap)))

View File

@ -11,8 +11,6 @@ import (
"strings"
"time"
"github.com/yandex-cloud/go-sdk/iamkey"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer/builder"
"github.com/hashicorp/packer/builder/yandex"
@ -28,6 +26,7 @@ const defaultStorageEndpoint = "storage.yandexcloud.net"
type Config struct {
common.PackerConfig `mapstructure:",squash"`
yandex.AccessConfig `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.
@ -36,7 +35,7 @@ type Config struct {
// 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.
// 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.
@ -53,13 +52,6 @@ type Config struct {
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"`
// OAuth token to use to authenticate to Yandex.Cloud. Alternatively you may set
// value by environment variable YC_TOKEN.
Token string `mapstructure:"token" required:"false"`
// Path to file with Service Account key in json format. This
// is an alternative method to authenticate to Yandex.Cloud. Alternatively you may set environment variable
// YC_SERVICE_ACCOUNT_KEY_FILE.
ServiceAccountKeyFile string `mapstructure:"service_account_key_file" required:"false"`
ctx interpolate.Context
}
@ -85,7 +77,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
return err
}
errs := new(packer.MultiError)
// Accumulate any errors
var errs *packer.MultiError
errs = packer.MultiErrorAppend(errs, p.config.AccessConfig.Prepare(&p.config.ctx)...)
if len(p.config.Paths) == 0 {
errs = packer.MultiErrorAppend(
@ -100,31 +95,6 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
}
}
// provision config by OS environment variables
if p.config.Token == "" {
p.config.Token = os.Getenv("YC_TOKEN")
}
if p.config.ServiceAccountKeyFile == "" {
p.config.ServiceAccountKeyFile = os.Getenv("YC_SERVICE_ACCOUNT_KEY_FILE")
}
if p.config.Token != "" && p.config.ServiceAccountKeyFile != "" {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("one of token or service account key file must be specified, not both"))
}
if p.config.Token != "" {
packer.LogSecretFilter.Set(p.config.Token)
}
if p.config.ServiceAccountKeyFile != "" {
if _, err := iamkey.ReadFromJSONFile(p.config.ServiceAccountKeyFile); err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("fail to read service account key file: %s", err))
}
}
if p.config.FolderID == "" {
p.config.FolderID = os.Getenv("YC_FOLDER_ID")
}
@ -203,8 +173,6 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
}
yandexConfig := ycSaneDefaults()
yandexConfig.Token = p.config.Token
yandexConfig.ServiceAccountKeyFile = p.config.ServiceAccountKeyFile
yandexConfig.DiskName = exporterName
yandexConfig.InstanceName = exporterName
yandexConfig.DiskSizeGb = p.config.DiskSizeGb
@ -221,7 +189,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
yandexConfig.PlatformID = p.config.PlatformID
}
driver, err := yandex.NewDriverYC(ui, &yandexConfig)
driver, err := yandex.NewDriverYC(ui, &p.config.AccessConfig)
if err != nil {
return nil, false, false, err
}

View File

@ -16,6 +16,10 @@ type FlatConfig struct {
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
Endpoint *string `mapstructure:"endpoint" required:"false" cty:"endpoint" hcl:"endpoint"`
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"`
@ -24,8 +28,6 @@ type FlatConfig struct {
PlatformID *string `mapstructure:"platform_id" required:"false" cty:"platform_id" hcl:"platform_id"`
SubnetID *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
Zone *string `mapstructure:"zone" required:"false" cty:"zone" hcl:"zone"`
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
ServiceAccountKeyFile *string `mapstructure:"service_account_key_file" required:"false" cty:"service_account_key_file" hcl:"service_account_key_file"`
}
// FlatMapstructure returns a new FlatConfig.
@ -47,6 +49,10 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"endpoint": &hcldec.AttrSpec{Name: "endpoint", Type: cty.String, Required: false},
"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},
@ -55,8 +61,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"platform_id": &hcldec.AttrSpec{Name: "platform_id", Type: 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},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"service_account_key_file": &hcldec.AttrSpec{Name: "service_account_key_file", Type: cty.String, Required: false},
}
return s
}

View File

@ -3,6 +3,7 @@ package yandexexport
import (
"testing"
"github.com/hashicorp/packer/builder/yandex"
"github.com/stretchr/testify/require"
"github.com/hashicorp/packer/helper/multistep"
@ -26,8 +27,10 @@ func TestPostProcessor_Configure(t *testing.T) {
name: "no one creds",
fields: fields{
config: Config{
Token: "",
ServiceAccountKeyFile: "",
AccessConfig: yandex.AccessConfig{
Token: "",
ServiceAccountKeyFile: "",
},
},
},
wantErr: false,
@ -36,8 +39,10 @@ func TestPostProcessor_Configure(t *testing.T) {
name: "both token and sa key file",
fields: fields{
config: Config{
Token: "some-value",
ServiceAccountKeyFile: "path/not-exist.file",
AccessConfig: yandex.AccessConfig{
Token: "some-value",
ServiceAccountKeyFile: "path/not-exist.file",
},
},
},
wantErr: true,
@ -46,8 +51,10 @@ func TestPostProcessor_Configure(t *testing.T) {
name: "use sa key file",
fields: fields{
config: Config{
Token: "",
ServiceAccountKeyFile: "testdata/fake-sa-key.json",
AccessConfig: yandex.AccessConfig{
Token: "",
ServiceAccountKeyFile: "testdata/fake-sa-key.json",
},
},
},
wantErr: false,

View File

@ -19,24 +19,18 @@ import (
yandexexport "github.com/hashicorp/packer/post-processor/yandex-export"
"github.com/hashicorp/packer/template/interpolate"
"github.com/yandex-cloud/go-genproto/yandex/cloud/iam/v1/awscompatibility"
"github.com/yandex-cloud/go-sdk/iamkey"
)
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`
// for operations 'upload' and 'delete' object to `bucket`.
ServiceAccountID string `mapstructure:"service_account_id" required:"true"`
// OAuth token to use to authenticate to Yandex.Cloud.
Token string `mapstructure:"token" required:"false"`
// Path to file with Service Account key in json format. This
// is an alternative method to authenticate to Yandex.Cloud.
ServiceAccountKeyFile string `mapstructure:"service_account_key_file" required:"false"`
// 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.
//
@ -49,7 +43,7 @@ type Config struct {
ObjectName string `mapstructure:"object_name" required:"false"`
// 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: `false`).
// 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
@ -85,27 +79,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
return err
}
errs := new(packer.MultiError)
// Accumulate any errors
var errs *packer.MultiError
// provision config by OS environment variables
if p.config.Token == "" {
p.config.Token = os.Getenv("YC_TOKEN")
}
if p.config.ServiceAccountKeyFile == "" {
p.config.ServiceAccountKeyFile = os.Getenv("YC_SERVICE_ACCOUNT_KEY_FILE")
}
if p.config.Token != "" {
packer.LogSecretFilter.Set(p.config.Token)
}
if p.config.ServiceAccountKeyFile != "" {
if _, err := iamkey.ReadFromJSONFile(p.config.ServiceAccountKeyFile); err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("fail to read service account key file: %s", err))
}
}
errs = packer.MultiErrorAppend(errs, p.config.AccessConfig.Prepare(&p.config.ctx)...)
if p.config.FolderID == "" {
p.config.FolderID = os.Getenv("YC_FOLDER_ID")
@ -160,12 +137,8 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
return nil, false, false, fmt.Errorf("error rendering object_name template: %s", err)
}
cfg := &yandex.Config{
Token: p.config.Token,
ServiceAccountKeyFile: p.config.ServiceAccountKeyFile,
}
client, err := yandex.NewDriverYC(ui, &p.config.AccessConfig)
client, err := yandex.NewDriverYC(ui, cfg)
if err != nil {
return nil, false, false, err
}

View File

@ -16,10 +16,12 @@ type FlatConfig struct {
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
Endpoint *string `mapstructure:"endpoint" required:"false" cty:"endpoint" hcl:"endpoint"`
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:"true" cty:"service_account_id" hcl:"service_account_id"`
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
ServiceAccountKeyFile *string `mapstructure:"service_account_key_file" required:"false" cty:"service_account_key_file" hcl:"service_account_key_file"`
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"`
@ -48,10 +50,12 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"endpoint": &hcldec.AttrSpec{Name: "endpoint", Type: cty.String, Required: false},
"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},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"service_account_key_file": &hcldec.AttrSpec{Name: "service_account_key_file", 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},

View File

@ -67,12 +67,15 @@ can be configured for this builder.
### Required:
@include 'builder/yandex/AccessConfig-required.mdx'
@include 'builder/yandex/Config-required.mdx'
### Optional:
@include 'builder/yandex/Config-not-required.mdx'
@include 'builder/yandex/AccessConfig-not-required.mdx'
@include 'builder/yandex/Config-not-required.mdx'
## Build template data

View File

@ -33,10 +33,14 @@ image.
### Required:
@include 'builder/yandex/AccessConfig-required.mdx'
@include 'post-processor/yandex-export/Config-required.mdx'
### Optional:
@include 'builder/yandex/AccessConfig-not-required.mdx'
@include 'post-processor/yandex-export/Config-not-required.mdx'
## Basic Example

View File

@ -25,10 +25,14 @@ file.
### Required:
@include 'builder/yandex/AccessConfig-required.mdx'
@include 'post-processor/yandex-import/Config-required.mdx'
### Optional:
@include 'builder/yandex/AccessConfig-not-required.mdx'
@include 'post-processor/yandex-import/Config-not-required.mdx'
## Basic Example

View File

@ -0,0 +1,9 @@
<!-- Code generated from the comments of the AccessConfig struct in builder/yandex/access_config.go; DO NOT EDIT MANUALLY -->
- `endpoint` (string) - Non standard API endpoint. Default is `api.cloud.yandex.net:443`.
- `service_account_key_file` (string) - Path to file with Service Account key in json format. This
is an alternative method to authenticate to Yandex.Cloud. Alternatively you may set environment variable
`YC_SERVICE_ACCOUNT_KEY_FILE`.
- `max_retries` (int) - The maximum number of times an API request is being executed.

View File

@ -0,0 +1,4 @@
<!-- Code generated from the comments of the AccessConfig struct in builder/yandex/access_config.go; DO NOT EDIT MANUALLY -->
- `token` (string) - OAuth token to use to authenticate to Yandex.Cloud. Alternatively you may set
value by environment variable `YC_TOKEN`.

View File

@ -0,0 +1,3 @@
<!-- Code generated from the comments of the AccessConfig struct in builder/yandex/access_config.go; DO NOT EDIT MANUALLY -->
AccessConfig is for common configuration related to Yandex.Cloud API access

View File

@ -1,12 +1,6 @@
<!-- Code generated from the comments of the Config struct in builder/yandex/config.go; DO NOT EDIT MANUALLY -->
- `endpoint` (string) - Non standard api endpoint URL.
- `service_account_key_file` (string) - Path to file with Service Account key in json format. This
is an alternative method to authenticate to Yandex.Cloud. Alternatively you may set environment variable
YC_SERVICE_ACCOUNT_KEY_FILE.
- `service_account_id` (string) - Service account identifier to assign to instance
- `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.
@ -19,8 +13,7 @@
- `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_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`.
@ -38,16 +31,14 @@
- `instance_name` (string) - The name assigned to the instance.
- `labels` (map[string]string) - Key/value pair labels to apply to
the launched 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`.
- `max_retries` (int) - The maximum number of times an API request is being executed
- `metadata` (map[string]string) - Metadata applied to the launched instance.
- `metadata_from_file` (map[string]string) - Metadata applied to the launched instance. Value are file paths.
- `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`.
@ -55,8 +46,7 @@
- `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.
- `source_image_id` (string) - The source image ID to use to create the new image from.
- `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`.

View File

@ -1,13 +1,10 @@
<!-- 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.
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'.
- `token` (string) - OAuth token to use to authenticate to Yandex.Cloud. Alternatively you may set
value by environment variable YC_TOKEN.
- `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`
source_image_family must be specified. Example: `ubuntu-1804-lts`.

View File

@ -11,10 +11,3 @@
zone in which the VM is launched.
- `zone` (string) - The name of the zone to launch the instance. This defaults to `ru-central1-a`.
- `token` (string) - OAuth token to use to authenticate to Yandex.Cloud. Alternatively you may set
value by environment variable YC_TOKEN.
- `service_account_key_file` (string) - Path to file with Service Account key in json format. This
is an alternative method to authenticate to Yandex.Cloud. Alternatively you may set environment variable
YC_SERVICE_ACCOUNT_KEY_FILE.

View File

@ -7,7 +7,7 @@
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.
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.

View File

@ -1,10 +1,5 @@
<!-- Code generated from the comments of the Config struct in post-processor/yandex-import/post-processor.go; DO NOT EDIT MANUALLY -->
- `token` (string) - OAuth token to use to authenticate to Yandex.Cloud.
- `service_account_key_file` (string) - Path to file with Service Account key in json format. This
is an alternative method to authenticate to Yandex.Cloud.
- `bucket` (string) - 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.
@ -17,7 +12,7 @@
- `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: `false`).
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.

View File

@ -3,4 +3,4 @@
- `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`
for operations 'upload' and 'delete' object to `bucket`.