266 lines
8.7 KiB
Go
266 lines
8.7 KiB
Go
//go:generate struct-markdown
|
|
//go:generate mapstructure-to-hcl2 -type Config
|
|
|
|
package scaleway
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
|
|
"github.com/hashicorp/packer/builder/scaleway/version"
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/common"
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/communicator"
|
|
packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/template/config"
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/template/interpolate"
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/useragent"
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/uuid"
|
|
"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"`
|
|
|
|
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 errs != nil && len(errs.Errors) > 0 {
|
|
return warnings, errs
|
|
}
|
|
|
|
packersdk.LogSecretFilter.Set(c.Token)
|
|
packersdk.LogSecretFilter.Set(c.SecretKey)
|
|
return warnings, nil
|
|
}
|