Adrien Delorme 0785c2f6fc
build using HCL2 (#8423)
This follows #8232 which added the code to generate the code required to parse
HCL files for each packer component.

All old config files of packer will keep on working the same. Packer takes one
argument. When a directory is passed, all files in the folder with a name
ending with  “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format.
When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed
using the HCL2 format. For every other case; the old packer style will be used.

## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files

I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields

## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file.

  This is a breaking change for packer plugins.

a packer component can be a: builder/provisioner/post-processor

each component interface now gets a `ConfigSpec() hcldec.ObjectSpec`
which allows packer to tell what is the layout of the hcl2 config meant
to configure that specific component.

This ObjectSpec is sent through the wire (RPC) and a cty.Value is now
sent through the already existing configuration entrypoints:

 Provisioner.Prepare(raws ...interface{}) error
 Builder.Prepare(raws ...interface{}) ([]string, error)
 PostProcessor.Configure(raws ...interface{}) error

close #1768


Example hcl files:

```hcl
// file amazon-ebs-kms-key/run.pkr.hcl
build {
    sources = [
        "source.amazon-ebs.first",
    ]

    provisioner "shell" {
        inline = [
            "sleep 5"
        ]
    }

    post-processor "shell-local" {
        inline = [
            "sleep 5"
        ]
    }
}

// amazon-ebs-kms-key/source.pkr.hcl

source "amazon-ebs" "first" {

    ami_name = "hcl2-test"
    region = "us-east-1"
    instance_type = "t2.micro"

    kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c"
    encrypt_boot = true
    source_ami_filter {
        filters {
          virtualization-type = "hvm"
          name =  "amzn-ami-hvm-????.??.?.????????-x86_64-gp2"
          root-device-type = "ebs"
        }
        most_recent = true
        owners = ["amazon"]
    }
    launch_block_device_mappings {
        device_name = "/dev/xvda"
        volume_size = 20
        volume_type = "gp2"
        delete_on_termination = "true"
    }
    launch_block_device_mappings {
        device_name = "/dev/xvdf"
        volume_size = 500
        volume_type = "gp2"
        delete_on_termination = true
        encrypted = true
    }

    ami_regions = ["eu-central-1"]
    run_tags {
        Name = "packer-solr-something"
        stack-name = "DevOps Tools"
    }
    
    communicator = "ssh"
    ssh_pty = true
    ssh_username = "ec2-user"
    associate_public_ip_address = true
}
```
2019-12-17 11:25:56 +01:00

169 lines
5.0 KiB
Go

//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type Config
package scaleway
import (
"errors"
"fmt"
"log"
"os"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/useragent"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/mapstructure"
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
// 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.
Token string `mapstructure:"api_token" required:"true"`
// 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
Organization string `mapstructure:"organization_id" required:"true"`
// The name of the region to launch the server in (par1
// or ams1). Consequently, this is the region where the snapshot will be
// available.
Region string `mapstructure:"region" required:"true"`
// 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.
Image string `mapstructure:"image" required:"true"`
// The name of the server commercial type:
// ARM64-128GB, ARM64-16GB, ARM64-2GB, ARM64-32GB, ARM64-4GB,
// ARM64-64GB, ARM64-8GB, C1, C2L, C2M, C2S, START1-L,
// START1-M, START1-S, START1-XS, X64-120GB, X64-15GB, X64-30GB,
// X64-60GB
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
ctx interpolate.Context
}
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
var md mapstructure.Metadata
err := config.Decode(c, &config.DecodeOpts{
Metadata: &md,
Interpolate: true,
InterpolateContext: &c.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"run_command",
},
},
}, raws...)
if err != nil {
return nil, err
}
c.UserAgent = useragent.String()
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.Token == "" {
c.Token = os.Getenv("SCALEWAY_API_TOKEN")
}
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 = "bootscript"
}
var errs *packer.MultiError
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
errs = packer.MultiErrorAppend(errs, es...)
}
if c.Organization == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("Scaleway Organization ID must be specified"))
}
if c.Token == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("Scaleway Token must be specified"))
}
if c.Region == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("region is required"))
}
if c.CommercialType == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("commercial type is required"))
}
if c.Image == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("image is required"))
}
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
}
packer.LogSecretFilter.Set(c.Token)
return nil, nil
}