Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions .web-docs/components/builder/instance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,14 @@ The configuration arguments for the builder. Arguments can either be required or

- `subnet` (string) - Subnet to create the instance within. Defaults to `default`.

- `name` (string) - Name of the temporary instance. Defaults to `packer-{{timestamp}}`.
- `name` (string) - Name of the temporary instance. Defaults to `packer-BUILD_NAME-RUN_ID` where
`BUILD_NAME` is the Packer source name and `RUN_ID` is a short prefix of the
unique ID Packer assigns to the current run. This must be unique to prevent
Oxide instance name conflicts.

- `hostname` (string) - Hostname of the temporary instance. Defaults to `packer-{{timestamp}}`.
- `hostname` (string) - Hostname of the temporary instance. Defaults to `packer-BUILD_NAME-RUN_ID`
where `BUILD_NAME` is the Packer source name and `RUN_ID` is a short prefix
of the unique ID Packer assigns to the current run.

- `cpus` (uint64) - Number of vCPUs to provision the instance with. Defaults to `1`.

Expand All @@ -74,18 +79,19 @@ The configuration arguments for the builder. Arguments can either be required or
- `ssh_public_keys` ([]string) - An array of names or IDs of SSH public keys to inject into the instance.

- `artifact_name` (string) - Name of the resulting image artifact. Defaults to
`SOURCE_IMAGE_NAME-{{timestamp}}` where `SOURCE_IMAGE_NAME` is the name of
the source image as retrieved from Oxide.
`SOURCE_IMAGE_NAME-BUILD_NAME-RUN_ID` where `SOURCE_IMAGE_NAME` is the name
of the source image as retrieved from Oxide, `BUILD_NAME` is the Packer
source name, and `RUN_ID` is a short prefix of the unique ID Packer assigns
to the current run.

- `artifact_description` (string) - Description of the resulting image artifact. Defaults to the description of
the source image as retrieved from Oxide.

- `artifact_os` (string) - Operating system of the resulting image artifact. Defaults to the OS of the
source image as retrieved from Oxide.

- `artifact_version` (string) - Version of the resulting image artifact. Defaults to
`SOURCE_IMAGE_VERSION-{{timestamp}}` where `SOURCE_IMAGE_VERSION` is the
version of the source image as retrieved from Oxide.
- `artifact_version` (string) - Version of the resulting image artifact. Defaults to the version of the
source image as retrieved from Oxide.

- `skip_create_image` (bool) - Skip creating the final image. When set to `true`, the build will boot the
temporary instance and run all provisioners but will not create a snapshot
Expand All @@ -105,6 +111,20 @@ The configuration arguments for the builder. Arguments can either be required or
<!-- End of code generated from the comments of the Config struct in component/builder/instance/config.go; -->


## Interpolation

This builder does not support Go template interpolation (e.g., `{{timestamp}}`).
Instead, use [HCL functions](/packer/docs/templates/hcl_templates/functions) to
compute dynamic values such as timestamped artifact names.

```hcl
# Interpolation: Unsupported
artifact_name = "packer-{{timestamp}}"

# HCL Functions: Supported
artifact_name = "packer-${formatdate("YYYY-MM-DD", timestamp())}"
```

## Communicator

A [`communicator`](/packer/docs/communicators) can be configured for the builder.
Expand Down
106 changes: 64 additions & 42 deletions component/builder/instance/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ package instance
import (
"errors"
"fmt"
"os"
"strings"

"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"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/uuid"
"github.com/mitchellh/mapstructure"
)

Expand Down Expand Up @@ -66,10 +68,15 @@ type Config struct {
// Subnet to create the instance within. Defaults to `default`.
Subnet string `mapstructure:"subnet"`

// Name of the temporary instance. Defaults to `packer-{{timestamp}}`.
// Name of the temporary instance. Defaults to `packer-BUILD_NAME-RUN_ID` where
// `BUILD_NAME` is the Packer source name and `RUN_ID` is a short prefix of the
// unique ID Packer assigns to the current run. This must be unique to prevent
// Oxide instance name conflicts.
Name string `mapstructure:"name"`

// Hostname of the temporary instance. Defaults to `packer-{{timestamp}}`.
// Hostname of the temporary instance. Defaults to `packer-BUILD_NAME-RUN_ID`
// where `BUILD_NAME` is the Packer source name and `RUN_ID` is a short prefix
// of the unique ID Packer assigns to the current run.
Hostname string `mapstructure:"hostname"`

// Number of vCPUs to provision the instance with. Defaults to `1`.
Expand All @@ -83,8 +90,10 @@ type Config struct {
SSHPublicKeys []string `mapstructure:"ssh_public_keys"`

// Name of the resulting image artifact. Defaults to
// `SOURCE_IMAGE_NAME-{{timestamp}}` where `SOURCE_IMAGE_NAME` is the name of
// the source image as retrieved from Oxide.
// `SOURCE_IMAGE_NAME-BUILD_NAME-RUN_ID` where `SOURCE_IMAGE_NAME` is the name
// of the source image as retrieved from Oxide, `BUILD_NAME` is the Packer
// source name, and `RUN_ID` is a short prefix of the unique ID Packer assigns
// to the current run.
ArtifactName string `mapstructure:"artifact_name"`

// Description of the resulting image artifact. Defaults to the description of
Expand All @@ -95,9 +104,8 @@ type Config struct {
// source image as retrieved from Oxide.
ArtifactOS string `mapstructure:"artifact_os"`

// Version of the resulting image artifact. Defaults to
// `SOURCE_IMAGE_VERSION-{{timestamp}}` where `SOURCE_IMAGE_VERSION` is the
// version of the source image as retrieved from Oxide.
// Version of the resulting image artifact. Defaults to the version of the
// source image as retrieved from Oxide.
ArtifactVersion string `mapstructure:"artifact_version"`

// Skip creating the final image. When set to `true`, the build will boot the
Expand All @@ -116,47 +124,28 @@ type Config struct {
// created, run `cloud-init status --wait` or an equivalent in a
// provisioner.
UserData string `mapstructure:"user_data" required:"false"`

ctx interpolate.Context
}

// Prepare decodes the configuration and validates it.
func (c *Config) Prepare(args ...any) ([]string, error) {
var metadata mapstructure.Metadata

if err := config.Decode(c, &config.DecodeOpts{
Metadata: &metadata,
Interpolate: true,
InterpolateContext: &c.ctx,
PluginType: BuilderID,
Metadata: &metadata,
Interpolate: false,
PluginType: BuilderID,
}, args...); err != nil {
return nil, fmt.Errorf("failed decoding configuration: %w", err)
}

// Set defaults.
{
if c.Name == "" {
name, err := interpolate.Render("packer-{{timestamp}}", nil)
if err != nil {
return nil, fmt.Errorf(
"failed rendering default name, this bug should be reported: %w",
err,
)
}

c.Name = name
c.Name = fmt.Sprintf("packer-%s", c.uniqueSuffix())
}

if c.Hostname == "" {
hostname, err := interpolate.Render("packer-{{timestamp}}", nil)
if err != nil {
return nil, fmt.Errorf(
"failed rendering default hostname, this bug should be reported: %w",
err,
)
}

c.Hostname = hostname
c.Hostname = fmt.Sprintf("packer-%s", c.uniqueSuffix())
}

if c.CPUs == 0 {
Expand Down Expand Up @@ -184,20 +173,12 @@ func (c *Config) Prepare(args ...any) ([]string, error) {
{
var multiErr *packer.MultiError

if errs := c.Comm.Prepare(&c.ctx); len(errs) > 0 {
if errs := c.Comm.Prepare(nil); len(errs) > 0 {
multiErr = packer.MultiErrorAppend(multiErr, errs...)
}

if c.Comm.SSHTemporaryKeyPairName == "" {
sshTemporaryKeyPairName, err := interpolate.Render("packer-{{timestamp}}", nil)
if err != nil {
return nil, fmt.Errorf(
"failed rendering default ssh temporary key pair name, this bug should be reported: %w",
err,
)
}

c.Comm.SSHTemporaryKeyPairName = sshTemporaryKeyPairName
c.Comm.SSHTemporaryKeyPairName = fmt.Sprintf("packer-%s", c.uniqueSuffix())
}

c.Comm.SSHTemporaryKeyPairType = "ed25519"
Expand Down Expand Up @@ -229,3 +210,44 @@ func (c *Config) Prepare(args ...any) ([]string, error) {

return nil, nil
}

// uniqueSuffix returns an identifier, derived from Packer-provided values,
// that is used to configure resource names that are unique and traceable to the
// Packer build that created them.
//
// The following Packer-provided values are used to generate the identifier.
//
// - [common.PackerConfig.PackerBuildName]: The build source name which is
// unique for each build in a Packer configuration.
// - PACKER_RUN_UUID: The unique ID Packer assigned to the current run, which is
// shared among the builds in a Packer configuration. When this is unset, it
// falls back to a generated UUID that's unique for each build. This value is
// truncated to 8 characters to keep the generated identifier within Oxide's
// 63-character name limit.
func (c *Config) uniqueSuffix() string {
runID := os.Getenv("PACKER_RUN_UUID")
if runID == "" {
runID = uuid.TimeOrderedUUID()
}

if len(runID) > 8 {
runID = runID[:8]
}

// Transform the Packer build name into a name that the Oxide API will accept.
// This is mainly here to transform `_` into `-`.
buildName := strings.Map(func(r rune) rune {
switch {
case r >= 'a' && r <= 'z',
r >= 'A' && r <= 'Z',
r >= '0' && r <= '9',
r == '-':
return r
default:
return '-'
}
}, c.PackerBuildName)
buildName = strings.Trim(buildName, "-")

return fmt.Sprintf("%s-%s", buildName, runID)
}
12 changes: 2 additions & 10 deletions component/builder/instance/step_image_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/oxidecomputer/oxide.go/oxide"
)

Expand Down Expand Up @@ -40,15 +39,8 @@ func (s *stepImageView) Run(ctx context.Context, stateBag multistep.StateBag) mu

stateBag.Put("source_image_id", string(image.Id))

timestamp, err := interpolate.Render("{{timestamp}}", &config.ctx)
if err != nil {
ui.Error("Failed rendering timestamp interpolation.")
stateBag.Put("error", err)
return multistep.ActionHalt
}

if config.ArtifactName == "" {
config.ArtifactName = fmt.Sprintf("%s-%s", image.Name, timestamp)
config.ArtifactName = fmt.Sprintf("%s-%s", image.Name, config.uniqueSuffix())
}

if config.ArtifactDescription == "" {
Expand All @@ -60,7 +52,7 @@ func (s *stepImageView) Run(ctx context.Context, stateBag multistep.StateBag) mu
}

if config.ArtifactVersion == "" {
config.ArtifactVersion = fmt.Sprintf("%s-%s", image.Version, timestamp)
config.ArtifactVersion = image.Version
}

return multistep.ActionContinue
Expand Down
4 changes: 3 additions & 1 deletion component/data-source/image/data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ func (d *Datasource) ConfigSpec() hcldec.ObjectSpec {
// the configuration is valid, and stores any necessary state for future methods
// to use during execution.
func (d *Datasource) Configure(args ...any) error {
if err := config.Decode(&d.config, nil, args...); err != nil {
if err := config.Decode(&d.config, &config.DecodeOpts{
Interpolate: false,
}, args...); err != nil {
return fmt.Errorf("failed decoding configuration: %w", err)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@

- `subnet` (string) - Subnet to create the instance within. Defaults to `default`.

- `name` (string) - Name of the temporary instance. Defaults to `packer-{{timestamp}}`.
- `name` (string) - Name of the temporary instance. Defaults to `packer-BUILD_NAME-RUN_ID` where
`BUILD_NAME` is the Packer source name and `RUN_ID` is a short prefix of the
unique ID Packer assigns to the current run. This must be unique to prevent
Oxide instance name conflicts.

- `hostname` (string) - Hostname of the temporary instance. Defaults to `packer-{{timestamp}}`.
- `hostname` (string) - Hostname of the temporary instance. Defaults to `packer-BUILD_NAME-RUN_ID`
where `BUILD_NAME` is the Packer source name and `RUN_ID` is a short prefix
of the unique ID Packer assigns to the current run.

- `cpus` (uint64) - Number of vCPUs to provision the instance with. Defaults to `1`.

Expand All @@ -35,18 +40,19 @@
- `ssh_public_keys` ([]string) - An array of names or IDs of SSH public keys to inject into the instance.

- `artifact_name` (string) - Name of the resulting image artifact. Defaults to
`SOURCE_IMAGE_NAME-{{timestamp}}` where `SOURCE_IMAGE_NAME` is the name of
the source image as retrieved from Oxide.
`SOURCE_IMAGE_NAME-BUILD_NAME-RUN_ID` where `SOURCE_IMAGE_NAME` is the name
of the source image as retrieved from Oxide, `BUILD_NAME` is the Packer
source name, and `RUN_ID` is a short prefix of the unique ID Packer assigns
to the current run.

- `artifact_description` (string) - Description of the resulting image artifact. Defaults to the description of
the source image as retrieved from Oxide.

- `artifact_os` (string) - Operating system of the resulting image artifact. Defaults to the OS of the
source image as retrieved from Oxide.

- `artifact_version` (string) - Version of the resulting image artifact. Defaults to
`SOURCE_IMAGE_VERSION-{{timestamp}}` where `SOURCE_IMAGE_VERSION` is the
version of the source image as retrieved from Oxide.
- `artifact_version` (string) - Version of the resulting image artifact. Defaults to the version of the
source image as retrieved from Oxide.

- `skip_create_image` (bool) - Skip creating the final image. When set to `true`, the build will boot the
temporary instance and run all provisioners but will not create a snapshot
Expand Down
14 changes: 14 additions & 0 deletions docs/builders/instance.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ Type: `oxide-instance`

@include 'component/builder/instance/Config-not-required.mdx'

## Interpolation

This builder does not support Go template interpolation (e.g., `{{timestamp}}`).
Instead, use [HCL functions](/packer/docs/templates/hcl_templates/functions) to
compute dynamic values such as timestamped artifact names.

```hcl
# Interpolation: Unsupported
artifact_name = "packer-{{timestamp}}"

# HCL Functions: Supported
artifact_name = "packer-${formatdate("YYYY-MM-DD", timestamp())}"
```

## Communicator

A [`communicator`](/packer/docs/communicators) can be configured for the builder.
Expand Down
Loading