diff --git a/datasource/hcp-packer-image/data.go b/datasource/hcp-packer-image/data.go index 75eb87ddf..3fe393548 100644 --- a/datasource/hcp-packer-image/data.go +++ b/datasource/hcp-packer-image/data.go @@ -4,6 +4,7 @@ package hcp_packer_image import ( "context" + "errors" "fmt" "log" "time" @@ -11,10 +12,12 @@ import ( "github.com/zclconf/go-cty/cty" "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/hcp-sdk-go/clients/cloud-packer-service/stable/2021-04-30/models" "github.com/hashicorp/packer-plugin-sdk/common" "github.com/hashicorp/packer-plugin-sdk/hcl2helper" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/template/config" + "github.com/hashicorp/packer/internal/registry" packerregistry "github.com/hashicorp/packer/internal/registry" ) @@ -26,7 +29,19 @@ type Config struct { common.PackerConfig `mapstructure:",squash"` // The name of the bucket your image is in. Bucket string `mapstructure:"bucket_name" required:"true"` + // The name of the channel to use when retrieving your image. + // Either this or `iteration_id` MUST be set. + // Mutually exclusive with `iteration_id`. + // If using several images from a single iteration, you may prefer + // sourcing an iteration first, and referencing it for subsequent uses, + // as every `hcp_packer_image` with the channel set will generate a + // potentially billable HCP Packer request, but if several + // `hcp_packer_image`s use a shared `hcp_packer_iteration` that will + // only generate one potentially billable request. + Channel string `mapstructure:"channel" required:"true"` // The name of the iteration Id to use when retrieving your image + // Either this or `channel` MUST be set. + // Mutually exclusive with `channel` IterationID string `mapstructure:"iteration_id" required:"true"` // The name of the cloud provider that your image is for. For example, // "aws" or "gce". @@ -53,12 +68,19 @@ func (d *Datasource) Configure(raws ...interface{}) error { if d.config.Bucket == "" { errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The `bucket_name` must be specified")) } - if d.config.IterationID == "" { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The `iteration_id`"+ - " must be specified. If you do not know your iteration_id, you "+ - "can retrieve it using the bucket name and desired channel using"+ - " the hcp-packer-iteration data source.")) + + // Ensure either channel or iteration_id are set, and not both at the same time + if d.config.Channel == "" && + d.config.IterationID == "" { + errs = packersdk.MultiErrorAppend(errs, errors.New( + "The `iteration_id` or `channel` must be specified.")) + } + if d.config.Channel != "" && + d.config.IterationID != "" { + errs = packersdk.MultiErrorAppend(errs, errors.New( + "`iteration_id` and `channel` cannot both be specified.")) } + if d.config.Region == "" { errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("`region` is "+ "currently a required field.")) @@ -109,6 +131,31 @@ func (d *Datasource) OutputSpec() hcldec.ObjectSpec { return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec() } +func (d *Datasource) getIteration( + ctx context.Context, + cli *registry.Client, +) (*models.HashicorpCloudPackerIteration, error) { + if d.config.IterationID != "" { + iter, err := cli.GetIteration(ctx, d.config.Bucket, packerregistry.GetIteration_byID(d.config.IterationID)) + if err != nil { + return nil, fmt.Errorf( + "error retrieving image iteration from HCP Packer registry: %s", + err) + } + + return iter, nil + } + + iter, err := cli.GetIterationFromChannel(ctx, d.config.Bucket, d.config.Channel) + if err != nil { + return nil, fmt.Errorf( + "error retrieving iteration from HCP Packer registry: %s", + err) + } + + return iter, nil +} + func (d *Datasource) Execute() (cty.Value, error) { ctx := context.TODO() @@ -121,10 +168,9 @@ func (d *Datasource) Execute() (cty.Value, error) { log.Printf("[INFO] Reading info from HCP Packer registry (%s) [project_id=%s, organization_id=%s, iteration_id=%s]", d.config.Bucket, cli.ProjectID, cli.OrganizationID, d.config.IterationID) - iteration, err := cli.GetIteration(ctx, d.config.Bucket, packerregistry.GetIteration_byID(d.config.IterationID)) + iteration, err := d.getIteration(ctx, cli) if err != nil { - return cty.NullVal(cty.EmptyObject), fmt.Errorf("error retrieving "+ - "image iteration from HCP Packer registry: %s", err.Error()) + return cty.NullVal(cty.EmptyObject), err } revokeAt := time.Time(iteration.RevokeAt) diff --git a/datasource/hcp-packer-image/data.hcl2spec.go b/datasource/hcp-packer-image/data.hcl2spec.go index 99f3239b3..3e7f6fa89 100644 --- a/datasource/hcp-packer-image/data.hcl2spec.go +++ b/datasource/hcp-packer-image/data.hcl2spec.go @@ -19,6 +19,7 @@ type FlatConfig struct { 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"` Bucket *string `mapstructure:"bucket_name" required:"true" cty:"bucket_name" hcl:"bucket_name"` + Channel *string `mapstructure:"channel" required:"true" cty:"channel" hcl:"channel"` IterationID *string `mapstructure:"iteration_id" required:"true" cty:"iteration_id" hcl:"iteration_id"` CloudProvider *string `mapstructure:"cloud_provider" required:"true" cty:"cloud_provider" hcl:"cloud_provider"` Region *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"` @@ -45,6 +46,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, "bucket_name": &hcldec.AttrSpec{Name: "bucket_name", Type: cty.String, Required: false}, + "channel": &hcldec.AttrSpec{Name: "channel", Type: cty.String, Required: false}, "iteration_id": &hcldec.AttrSpec{Name: "iteration_id", Type: cty.String, Required: false}, "cloud_provider": &hcldec.AttrSpec{Name: "cloud_provider", Type: cty.String, Required: false}, "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, diff --git a/website/content/partials/datasource/hcp-packer-image/Config-required.mdx b/website/content/partials/datasource/hcp-packer-image/Config-required.mdx index 64c3343c2..a1395b0f6 100644 --- a/website/content/partials/datasource/hcp-packer-image/Config-required.mdx +++ b/website/content/partials/datasource/hcp-packer-image/Config-required.mdx @@ -2,7 +2,19 @@ - `bucket_name` (string) - The name of the bucket your image is in. +- `channel` (string) - The name of the channel to use when retrieving your image. + Either this or `iteration_id` MUST be set. + Mutually exclusive with `iteration_id`. + If using several images from a single iteration, you may prefer + sourcing an iteration first, and referencing it for subsequent uses, + as every `hcp_packer_image` with the channel set will generate a + potentially billable HCP Packer request, but if several + `hcp_packer_image`s use a shared `hcp_packer_iteration` that will + only generate one potentially billable request. + - `iteration_id` (string) - The name of the iteration Id to use when retrieving your image + Either this or `channel` MUST be set. + Mutually exclusive with `channel` - `cloud_provider` (string) - The name of the cloud provider that your image is for. For example, "aws" or "gce".