diff --git a/datasource/hcp-packer-image/data.go b/datasource/hcp-packer-image/data.go index 75eb87ddf13..8473f792551 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,8 +29,22 @@ 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. + // + // If using several images from a single iteration, you may prefer + // sourcing an iteration first, and referencing it for subsequent uses, + // as getting the iteration induces a potentially billable request. + // + // Either this or `iteration_id` MUST be set. + // + // Mutually exclusive with `iteration_id` + Channel string `mapstructure:"channel" required:"false"` // The name of the iteration Id to use when retrieving your image - IterationID string `mapstructure:"iteration_id" required:"true"` + // + // Either this or `channel` MUST be set. + // + // Mutually exclusive with `channel` + IterationID string `mapstructure:"iteration_id" required:"false"` // The name of the cloud provider that your image is for. For example, // "aws" or "gce". CloudProvider string `mapstructure:"cloud_provider" required:"true"` @@ -53,12 +70,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 +133,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 +170,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 99f3239b393..b59147c738b 100644 --- a/datasource/hcp-packer-image/data.hcl2spec.go +++ b/datasource/hcp-packer-image/data.hcl2spec.go @@ -19,7 +19,8 @@ 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"` - IterationID *string `mapstructure:"iteration_id" required:"true" cty:"iteration_id" hcl:"iteration_id"` + Channel *string `mapstructure:"channel" required:"false" cty:"channel" hcl:"channel"` + IterationID *string `mapstructure:"iteration_id" required:"false" 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-not-required.mdx b/website/content/partials/datasource/hcp-packer-image/Config-not-required.mdx new file mode 100644 index 00000000000..0f5ce048022 --- /dev/null +++ b/website/content/partials/datasource/hcp-packer-image/Config-not-required.mdx @@ -0,0 +1,19 @@ + + +- `channel` (string) - The name of the channel to use when retrieving your image. + + If using several images from a single iteration, you may prefer + sourcing an iteration first, and referencing it for subsequent uses, + as getting the iteration induces a potentially billable request. + + Either this or `iteration_id` MUST be set. + + Mutually exclusive with `iteration_id` + +- `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` + + 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 64c3343c259..f673db0fb05 100644 --- a/website/content/partials/datasource/hcp-packer-image/Config-required.mdx +++ b/website/content/partials/datasource/hcp-packer-image/Config-required.mdx @@ -2,8 +2,6 @@ - `bucket_name` (string) - The name of the bucket your image is in. -- `iteration_id` (string) - The name of the iteration Id to use when retrieving your image - - `cloud_provider` (string) - The name of the cloud provider that your image is for. For example, "aws" or "gce".