packer-cn/builder/scaleway/config.go

272 lines
8.8 KiB
Go

//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type Config
package scaleway
import (
"errors"
"fmt"
"log"
"os"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/communicator"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/hashicorp/packer-plugin-sdk/useragent"
"github.com/hashicorp/packer-plugin-sdk/uuid"
"github.com/hashicorp/packer/builder/scaleway/version"
"github.com/mitchellh/mapstructure"
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
"github.com/scaleway/scaleway-sdk-go/scw"
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
// The AccessKey corresponding to the secret key.
// Will be fetched first from the [scaleway configuration file](https://github.com/scaleway/scaleway-sdk-go/blob/master/scw/README.md).
// It can also be specified via the environment variable SCW_ACCESS_KEY.
AccessKey string `mapstructure:"access_key" required:"true"`
// The SecretKey to authenticate against the Scaleway API.
// Will be fetched first from the [scaleway configuration file](https://github.com/scaleway/scaleway-sdk-go/blob/master/scw/README.md).
// It can also be specified via the environment variable SCW_SECRET_KEY.
SecretKey string `mapstructure:"secret_key" required:"true"`
// The Project ID in which the instances, volumes and snapshots will be created.
// Will be fetched first from the [scaleway configuration file](https://github.com/scaleway/scaleway-sdk-go/blob/master/scw/README.md).
// It can also be specified via the environment variable SCW_DEFAULT_PROJECT_ID.
ProjectID string `mapstructure:"project_id" required:"true"`
// The Zone in which the instances, volumes and snapshots will be created.
// Will be fetched first from the [scaleway configuration file](https://github.com/scaleway/scaleway-sdk-go/blob/master/scw/README.md).
// It can also be specified via the environment variable SCW_DEFAULT_ZONE
Zone string `mapstructure:"zone" required:"true"`
// The Scaleway API URL to use
// Will be fetched first from the [scaleway configuration file](https://github.com/scaleway/scaleway-sdk-go/blob/master/scw/README.md).
// It can also be specified via the environment variable SCW_API_URL
APIURL string `mapstructure:"api_url"`
// The UUID of the base image to use. This is the image
// that will be used to launch a new server and provision it. See
// the images list
// get the complete list of the accepted image UUID.
// The marketplace image label (eg `ubuntu_focal`) also works.
Image string `mapstructure:"image" required:"true"`
// The name of the server commercial type:
// C1, C2L, C2M, C2S, DEV1-S, DEV1-M, DEV1-L, DEV1-XL,
// GP1-XS, GP1-S, GP1-M, GP1-L, GP1-XL, RENDER-S
CommercialType string `mapstructure:"commercial_type" required:"true"`
// The name of the resulting snapshot that will
// appear in your account. Default packer-TIMESTAMP
SnapshotName string `mapstructure:"snapshot_name" required:"false"`
// The name of the resulting image that will appear in
// your account. Default packer-TIMESTAMP
ImageName string `mapstructure:"image_name" required:"false"`
// The name assigned to the server. Default
// packer-UUID
ServerName string `mapstructure:"server_name" required:"false"`
// The id of an existing bootscript to use when
// booting the server.
Bootscript string `mapstructure:"bootscript" required:"false"`
// The type of boot, can be either local or
// bootscript, Default bootscript
BootType string `mapstructure:"boottype" required:"false"`
RemoveVolume bool `mapstructure:"remove_volume"`
// Shutdown timeout. Default to 5m
ShutdownTimeout string `mapstructure:"shutdown_timeout" required:"false"`
UserAgent string `mapstructure-to-hcl2:",skip"`
ctx interpolate.Context
// Deprecated configs
// The token to use to authenticate with your account.
// It can also be specified via environment variable SCALEWAY_API_TOKEN. You
// can see and generate tokens in the "Credentials"
// section of the control panel.
// Deprecated, use SecretKey instead
Token string `mapstructure:"api_token" required:"false"`
// The organization id to use to identify your
// organization. It can also be specified via environment variable
// SCALEWAY_ORGANIZATION. Your organization id is available in the
// "Account" section of the
// control panel.
// Previously named: api_access_key with environment variable: SCALEWAY_API_ACCESS_KEY
// Deprecated, use ProjectID instead
Organization string `mapstructure:"organization_id" required:"false"`
// The name of the region to launch the server in (par1
// or ams1). Consequently, this is the region where the snapshot will be
// available.
// Deprecated, use Zone instead
Region string `mapstructure:"region" required:"false"`
}
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
var md mapstructure.Metadata
err := config.Decode(c, &config.DecodeOpts{
Metadata: &md,
PluginType: BuilderId,
Interpolate: true,
InterpolateContext: &c.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"run_command",
},
},
}, raws...)
if err != nil {
return nil, err
}
var warnings []string
c.UserAgent = useragent.String(version.ScalewayPluginVersion.FormattedVersion())
configFile, err := scw.LoadConfig()
// If the config file do not exist, don't return an error as we may find config in ENV or flags.
if _, isNotFoundError := err.(*scw.ConfigFileNotFoundError); isNotFoundError {
configFile = &scw.Config{}
} else if err != nil {
return nil, err
}
activeProfile, err := configFile.GetActiveProfile()
if err != nil {
return nil, err
}
envProfile := scw.LoadEnvProfile()
profile := scw.MergeProfiles(activeProfile, envProfile)
// Deprecated variables
if c.Organization == "" {
if os.Getenv("SCALEWAY_ORGANIZATION") != "" {
c.Organization = os.Getenv("SCALEWAY_ORGANIZATION")
} else {
log.Printf("Deprecation warning: Use SCALEWAY_ORGANIZATION environment variable and organization_id argument instead of api_access_key argument and SCALEWAY_API_ACCESS_KEY environment variable.")
c.Organization = os.Getenv("SCALEWAY_API_ACCESS_KEY")
}
}
if c.Organization != "" {
warnings = append(warnings, "organization_id is deprecated in favor of project_id")
c.ProjectID = c.Organization
}
if c.Token == "" {
c.Token = os.Getenv("SCALEWAY_API_TOKEN")
}
if c.Token != "" {
warnings = append(warnings, "token is deprecated in favor of secret_key")
c.SecretKey = c.Token
}
if c.Region != "" {
warnings = append(warnings, "region is deprecated in favor of zone")
c.Zone = c.Region
}
if c.AccessKey == "" {
if profile.AccessKey != nil {
c.AccessKey = *profile.AccessKey
}
}
if c.SecretKey == "" {
if profile.SecretKey != nil {
c.SecretKey = *profile.SecretKey
}
}
if c.ProjectID == "" {
if profile.DefaultProjectID != nil {
c.ProjectID = *profile.DefaultProjectID
}
}
if c.Zone == "" {
if profile.DefaultZone != nil {
c.Zone = *profile.DefaultZone
}
}
if c.APIURL == "" {
if profile.APIURL != nil {
c.APIURL = *profile.APIURL
}
}
if c.SnapshotName == "" {
def, err := interpolate.Render("snapshot-packer-{{timestamp}}", nil)
if err != nil {
panic(err)
}
c.SnapshotName = def
}
if c.ImageName == "" {
def, err := interpolate.Render("image-packer-{{timestamp}}", nil)
if err != nil {
panic(err)
}
c.ImageName = def
}
if c.ServerName == "" {
// Default to packer-[time-ordered-uuid]
c.ServerName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
}
if c.BootType == "" {
c.BootType = instance.BootTypeLocal.String()
}
var errs *packersdk.MultiError
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
errs = packersdk.MultiErrorAppend(errs, es...)
}
if c.ProjectID == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("scaleway Project ID must be specified"))
}
if c.SecretKey == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("scaleway Secret Key must be specified"))
}
if c.AccessKey == "" {
warnings = append(warnings, "access_key will be required in future versions")
c.AccessKey = "SCWXXXXXXXXXXXXXXXXX"
}
if c.Zone == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("Scaleway Zone is required"))
}
if c.CommercialType == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("commercial type is required"))
}
if c.Image == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("image is required"))
}
if c.ShutdownTimeout == "" {
c.ShutdownTimeout = "5m"
}
if errs != nil && len(errs.Errors) > 0 {
return warnings, errs
}
packersdk.LogSecretFilter.Set(c.Token)
packersdk.LogSecretFilter.Set(c.SecretKey)
return warnings, nil
}