From fd80f8da8c7237328dcbb4489d98106747de259f Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 1 Sep 2021 16:57:36 -0700 Subject: [PATCH] Add two new data sources -- hcp-packer-iteration and hcp-packer-image. These data sources together will allow users to query hcp_packer for the image_ids they need to use as source images to their builds, with a simple UI and clear outputs. add quick and dirty acceptance test for hcp packer image iteration and hcp packer image data sources PR review linting --- command/plugin.go | 4 + datasource/hcp-packer-image/data.go | 147 ++++++++++++++++++ datasource/hcp-packer-image/data.hcl2spec.go | 90 +++++++++++ datasource/hcp-packer-iteration/data.go | 122 +++++++++++++++ .../hcp-packer-iteration/data.hcl2spec.go | 86 ++++++++++ .../hcp-packer-iteration/data_acc_test.go | 43 +++++ .../test-fixtures/template.pkr.hcl | 33 ++++ go.mod | 137 ---------------- internal/packer_registry/registry_service.go | 19 +++ .../hcp-packer-image/Config-required.mdx | 12 ++ .../hcp-packer-image/DatasourceOutput.mdx | 26 ++++ .../hcp-packer-iteration/Config-required.mdx | 7 + .../hcp-packer-iteration/DatasourceOutput.mdx | 30 ++++ 13 files changed, 619 insertions(+), 137 deletions(-) create mode 100644 datasource/hcp-packer-image/data.go create mode 100644 datasource/hcp-packer-image/data.hcl2spec.go create mode 100644 datasource/hcp-packer-iteration/data.go create mode 100644 datasource/hcp-packer-iteration/data.hcl2spec.go create mode 100644 datasource/hcp-packer-iteration/data_acc_test.go create mode 100644 datasource/hcp-packer-iteration/test-fixtures/template.pkr.hcl create mode 100644 website/content/partials/datasource/hcp-packer-image/Config-required.mdx create mode 100644 website/content/partials/datasource/hcp-packer-image/DatasourceOutput.mdx create mode 100644 website/content/partials/datasource/hcp-packer-iteration/Config-required.mdx create mode 100644 website/content/partials/datasource/hcp-packer-iteration/DatasourceOutput.mdx diff --git a/command/plugin.go b/command/plugin.go index e8a42c41b..a7158678d 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -15,6 +15,8 @@ import ( filebuilder "github.com/hashicorp/packer/builder/file" nullbuilder "github.com/hashicorp/packer/builder/null" + hcppackerimagedatasource "github.com/hashicorp/packer/datasource/hcp-packer-image" + hcppackeriterationdatasource "github.com/hashicorp/packer/datasource/hcp-packer-iteration" packerimageiterationdatasource "github.com/hashicorp/packer/datasource/packer-image-iteration" artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice" checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum" @@ -60,6 +62,8 @@ var PostProcessors = map[string]packersdk.PostProcessor{ } var Datasources = map[string]packersdk.Datasource{ + "hcp-packer-image": new(hcppackerimagedatasource.Datasource), + "hcp-packer-iteration": new(hcppackeriterationdatasource.Datasource), "packer-image-iteration": new(packerimageiterationdatasource.Datasource), } diff --git a/datasource/hcp-packer-image/data.go b/datasource/hcp-packer-image/data.go new file mode 100644 index 000000000..11b2af26f --- /dev/null +++ b/datasource/hcp-packer-image/data.go @@ -0,0 +1,147 @@ +//go:generate packer-sdc struct-markdown +//go:generate packer-sdc mapstructure-to-hcl2 -type DatasourceOutput,Config +package hcp_packer_image + +import ( + "context" + "fmt" + "log" + + "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/hcl/v2/hcldec" + "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" + packerregistry "github.com/hashicorp/packer/internal/packer_registry" +) + +type Datasource struct { + config Config +} + +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 iteration Id to use when retrieving your image + IterationID string `mapstructure:"iteration_id" required:"true"` + // The name of the cloud provider that your image is for. For example, + // "aws" or "gce". + CloudProvider string `mapstructure:"cloud_provider" required:"true"` + // The name of the cloud region your image is in. For example "us-east-1". + Region string `mapstructure:"region" required:"true"` + // TODO: Version string `mapstructure:"version"` + // TODO: Fingerprint string `mapstructure:"fingerprint"` + // TODO: Label string `mapstructure:"label"` +} + +func (d *Datasource) ConfigSpec() hcldec.ObjectSpec { + return d.config.FlatMapstructure().HCL2Spec() +} + +func (d *Datasource) Configure(raws ...interface{}) error { + err := config.Decode(&d.config, nil, raws...) + if err != nil { + return err + } + + var errs *packersdk.MultiError + + 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.")) + } + if d.config.Region == "" { + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("`region` is "+ + "currently a required field.")) + } + if d.config.CloudProvider == "" { + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("`cloud_provider` is "+ + "currently a required field.")) + } + + if errs != nil && len(errs.Errors) > 0 { + return errs + } + return nil +} + +// Information from []*models.HashicorpCloudPackerImage with some information +// from the parent []*models.HashicorpCloudPackerBuild included where it seemed +// like it might be relevant. Need to copy so we can generate +type DatasourceOutput struct { + // The name of the cloud provider that the image exists in. For example, + // "aws", "azure", or "gce". + CloudProvider string `mapstructure:"cloud_provider"` + // The specific Packer builder or post-processor used to create the image. + ComponentType string `mapstructure:"component_type"` + // The date and time at which the image was created. + CreatedAt string `mapstructure:"created_at"` + // The id of the build that created the image. This is a ULID, which is a + // unique identifier similar to a UUID. It is created by the HCP Packer + // Registry when an build is first created, and is unique to this build. + BuildID string `mapstructure:"build_id"` + // The iteration id. This is a ULID, which is a unique identifier similar + // to a UUID. It is created by the HCP Packer Registry when an iteration is + // first created, and is unique to this iteration. + IterationID string `mapstructure:"iteration_id"` + // The UUID associated with the Packer run that created this image. + PackerRunUUID string `mapstructure:"packer_run_uuid"` + // ID or URL of the remote cloud image as given by a build. + ID string `mapstructure:"id"` + // The cloud region as given by `packer build`. eg. "ap-east-1". + // For locally managed clouds, this may map instead to a cluster, server + // or datastore. + Region string `mapstructure:"region"` +} + +func (d *Datasource) OutputSpec() hcldec.ObjectSpec { + return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec() +} + +func (d *Datasource) Execute() (cty.Value, error) { + cli, err := packerregistry.NewClient() + if err != nil { + return cty.NullVal(cty.EmptyObject), err + } + // Load channel. + 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 := packerregistry.GetIterationFromId(context.TODO(), cli, d.config.Bucket, d.config.IterationID) + if err != nil { + return cty.NullVal(cty.EmptyObject), fmt.Errorf("error retrieving "+ + "image iteration from HCP Packer registry: %s", err.Error()) + } + + output := DatasourceOutput{} + + for _, build := range iteration.Builds { + if build.CloudProvider == d.config.CloudProvider { + for _, image := range build.Images { + if image.Region == d.config.Region { + // This is the desired image. + output = DatasourceOutput{ + CloudProvider: build.CloudProvider, + ComponentType: build.ComponentType, + CreatedAt: image.CreatedAt.String(), + BuildID: build.ID, + IterationID: build.IterationID, + PackerRunUUID: build.PackerRunUUID, + ID: image.ImageID, + Region: image.Region, + } + } + } + } + } + + return hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()), nil +} diff --git a/datasource/hcp-packer-image/data.hcl2spec.go b/datasource/hcp-packer-image/data.hcl2spec.go new file mode 100644 index 000000000..9506bf06b --- /dev/null +++ b/datasource/hcp-packer-image/data.hcl2spec.go @@ -0,0 +1,90 @@ +// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. + +package hcp_packer_image + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` + PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` + 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"` + CloudProvider *string `mapstructure:"cloud_provider" required:"true" cty:"cloud_provider" hcl:"cloud_provider"` + Region *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "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}, + "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}, + } + return s +} + +// FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatDatasourceOutput struct { + CloudProvider *string `mapstructure:"cloud_provider" cty:"cloud_provider" hcl:"cloud_provider"` + ComponentType *string `mapstructure:"component_type" cty:"component_type" hcl:"component_type"` + CreatedAt *string `mapstructure:"created_at" cty:"created_at" hcl:"created_at"` + BuildID *string `mapstructure:"build_id" cty:"build_id" hcl:"build_id"` + IterationID *string `mapstructure:"iteration_id" cty:"iteration_id" hcl:"iteration_id"` + PackerRunUUID *string `mapstructure:"packer_run_uuid" cty:"packer_run_uuid" hcl:"packer_run_uuid"` + ID *string `mapstructure:"id" cty:"id" hcl:"id"` + Region *string `mapstructure:"region" cty:"region" hcl:"region"` +} + +// FlatMapstructure returns a new FlatDatasourceOutput. +// FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*DatasourceOutput) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatDatasourceOutput) +} + +// HCL2Spec returns the hcl spec of a DatasourceOutput. +// This spec is used by HCL to read the fields of DatasourceOutput. +// The decoded values from this spec will then be applied to a FlatDatasourceOutput. +func (*FlatDatasourceOutput) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "cloud_provider": &hcldec.AttrSpec{Name: "cloud_provider", Type: cty.String, Required: false}, + "component_type": &hcldec.AttrSpec{Name: "component_type", Type: cty.String, Required: false}, + "created_at": &hcldec.AttrSpec{Name: "created_at", Type: cty.String, Required: false}, + "build_id": &hcldec.AttrSpec{Name: "build_id", Type: cty.String, Required: false}, + "iteration_id": &hcldec.AttrSpec{Name: "iteration_id", Type: cty.String, Required: false}, + "packer_run_uuid": &hcldec.AttrSpec{Name: "packer_run_uuid", Type: cty.String, Required: false}, + "id": &hcldec.AttrSpec{Name: "id", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + } + return s +} diff --git a/datasource/hcp-packer-iteration/data.go b/datasource/hcp-packer-iteration/data.go new file mode 100644 index 000000000..87ae3fd76 --- /dev/null +++ b/datasource/hcp-packer-iteration/data.go @@ -0,0 +1,122 @@ +//go:generate packer-sdc struct-markdown +//go:generate packer-sdc mapstructure-to-hcl2 -type DatasourceOutput,Config +package hcp_packer_iteration + +import ( + "context" + "fmt" + "log" + + "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/hcl/v2/hcldec" + "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" + packerregistry "github.com/hashicorp/packer/internal/packer_registry" +) + +type Datasource struct { + config Config +} + +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 + Channel string `mapstructure:"channel" required:"true"` + // TODO: Version string `mapstructure:"version"` + // TODO: Fingerprint string `mapstructure:"fingerprint"` + // TODO: Label string `mapstructure:"label"` +} + +func (d *Datasource) ConfigSpec() hcldec.ObjectSpec { + return d.config.FlatMapstructure().HCL2Spec() +} + +func (d *Datasource) Configure(raws ...interface{}) error { + err := config.Decode(&d.config, nil, raws...) + if err != nil { + return err + } + + var errs *packersdk.MultiError + + if d.config.Bucket == "" { + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The `bucket_name` must be specified")) + } + if d.config.Channel == "" { + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("`channel` is currently a required field.")) + } + + if errs != nil && len(errs.Errors) > 0 { + return errs + } + return nil +} + +// Essentially a copy of []*models.HashicorpCloudPackerIteration, but without the +// []Builds or ancestor id. +type DatasourceOutput struct { + // who created the iteration + AuthorID string `mapstructure:"author_id"` + // Name of the bucket that the iteration was retrieved from + BucketName string `mapstructure:"bucket_name"` + // If true, this iteration is considered "ready to use" and will be + // returned even if the include_incomplete flag is "false" in the + // list iterations request. Note that if you are retrieving an iteration + // using a channel, this will always be "true"; channels cannot be assigned + // to incomplete iterations. + Complete bool `mapstructure:"complete"` + // The date the iteration was created. + CreatedAt string `mapstructure:"created_at"` + // The fingerprint of the build; this could be a git sha or other unique + // identifier as set by the Packer build that created this iteration. + Fingerprint string `mapstructure:"fingerprint"` + // The iteration id. This is a ULID, which is a unique identifier similar + // to a UUID. It is created by the HCP Packer Registry when an iteration is + // first created, and is unique to this iteration. + ID string `mapstructure:"id"` + // The version number assigned to an iteration. This number is an integer, + // and is created by the HCP Packer Registry once an iteration is + // marked "complete". If a new iteration is marked "complete", the version + // that HCP Packer assigns to it will always be the highest previous + // iteration version plus one. + IncrementalVersion int32 `mapstructure:"incremental_version"` + // The date when this iteration was last updated. + UpdatedAt string `mapstructure:"updated_at"` +} + +func (d *Datasource) OutputSpec() hcldec.ObjectSpec { + return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec() +} + +func (d *Datasource) Execute() (cty.Value, error) { + cli, err := packerregistry.NewClient() + if err != nil { + return cty.NullVal(cty.EmptyObject), err + } + // Load channel. + log.Printf("[INFO] Reading iteration info from HCP Packer registry (%s) [project_id=%s, organization_id=%s, channel=%s]", + d.config.Bucket, cli.ProjectID, cli.OrganizationID, d.config.Channel) + + iteration, err := packerregistry.GetIterationFromChannel(context.TODO(), cli, d.config.Bucket, d.config.Channel) + if err != nil { + return cty.NullVal(cty.EmptyObject), fmt.Errorf("error retrieving "+ + "iteration from HCP Packer registry: %s", err.Error()) + } + output := DatasourceOutput{ + AuthorID: iteration.AuthorID, + BucketName: iteration.BucketSlug, + Complete: iteration.Complete, + CreatedAt: iteration.CreatedAt.String(), + Fingerprint: iteration.Fingerprint, + ID: iteration.ID, + IncrementalVersion: iteration.IncrementalVersion, + UpdatedAt: iteration.UpdatedAt.String(), + } + + return hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()), nil +} diff --git a/datasource/hcp-packer-iteration/data.hcl2spec.go b/datasource/hcp-packer-iteration/data.hcl2spec.go new file mode 100644 index 000000000..6fe9249f4 --- /dev/null +++ b/datasource/hcp-packer-iteration/data.hcl2spec.go @@ -0,0 +1,86 @@ +// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. + +package hcp_packer_iteration + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` + PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` + 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"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "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}, + } + return s +} + +// FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatDatasourceOutput struct { + AuthorID *string `mapstructure:"author_id" cty:"author_id" hcl:"author_id"` + BucketName *string `mapstructure:"bucket_name" cty:"bucket_name" hcl:"bucket_name"` + Complete *bool `mapstructure:"complete" cty:"complete" hcl:"complete"` + CreatedAt *string `mapstructure:"created_at" cty:"created_at" hcl:"created_at"` + Fingerprint *string `mapstructure:"fingerprint" cty:"fingerprint" hcl:"fingerprint"` + ID *string `mapstructure:"id" cty:"id" hcl:"id"` + IncrementalVersion *int32 `mapstructure:"incremental_version" cty:"incremental_version" hcl:"incremental_version"` + UpdatedAt *string `mapstructure:"updated_at" cty:"updated_at" hcl:"updated_at"` +} + +// FlatMapstructure returns a new FlatDatasourceOutput. +// FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*DatasourceOutput) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatDatasourceOutput) +} + +// HCL2Spec returns the hcl spec of a DatasourceOutput. +// This spec is used by HCL to read the fields of DatasourceOutput. +// The decoded values from this spec will then be applied to a FlatDatasourceOutput. +func (*FlatDatasourceOutput) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "author_id": &hcldec.AttrSpec{Name: "author_id", Type: cty.String, Required: false}, + "bucket_name": &hcldec.AttrSpec{Name: "bucket_name", Type: cty.String, Required: false}, + "complete": &hcldec.AttrSpec{Name: "complete", Type: cty.Bool, Required: false}, + "created_at": &hcldec.AttrSpec{Name: "created_at", Type: cty.String, Required: false}, + "fingerprint": &hcldec.AttrSpec{Name: "fingerprint", Type: cty.String, Required: false}, + "id": &hcldec.AttrSpec{Name: "id", Type: cty.String, Required: false}, + "incremental_version": &hcldec.AttrSpec{Name: "incremental_version", Type: cty.Number, Required: false}, + "updated_at": &hcldec.AttrSpec{Name: "updated_at", Type: cty.String, Required: false}, + } + return s +} diff --git a/datasource/hcp-packer-iteration/data_acc_test.go b/datasource/hcp-packer-iteration/data_acc_test.go new file mode 100644 index 000000000..def8d5521 --- /dev/null +++ b/datasource/hcp-packer-iteration/data_acc_test.go @@ -0,0 +1,43 @@ +package hcp_packer_iteration + +import ( + _ "embed" + "fmt" + "os/exec" + "testing" + + "github.com/hashicorp/packer-plugin-sdk/acctest" +) + +//go:embed test-fixtures/template.pkr.hcl +var testDatasourceBasic string + +// Acceptance tests for data sources. +// +// To be successful, the HCP project you're providing credentials for must +// contain a bucket named "hardened-ubuntu-16-04", with a channel named +// "packer-acc-test". It must contain a build that references an image in AWS +// region "us-east-1". Your HCP credentials must be provided through your +// runtime environment because the template this test uses does not set them. +// +// TODO: update this acceptance to create and clean up the HCP resources this +// data source queries, to prevent plugin developers from having to have images +// as defined above. + +func TestAccDatasource_HCPPackerIteration(t *testing.T) { + testCase := &acctest.PluginTestCase{ + Name: "hcp_packer_iteration_datasource_basic_test", + Template: testDatasourceBasic, + // TODO have acc test write iteration id to a file and check it to make + // sure it isn't empty. + Check: func(buildCommand *exec.Cmd, logfile string) error { + if buildCommand.ProcessState != nil { + if buildCommand.ProcessState.ExitCode() != 0 { + return fmt.Errorf("Bad exit code. Logfile: %s", logfile) + } + } + return nil + }, + } + acctest.TestPlugin(t, testCase) +} diff --git a/datasource/hcp-packer-iteration/test-fixtures/template.pkr.hcl b/datasource/hcp-packer-iteration/test-fixtures/template.pkr.hcl new file mode 100644 index 000000000..52bac83ee --- /dev/null +++ b/datasource/hcp-packer-iteration/test-fixtures/template.pkr.hcl @@ -0,0 +1,33 @@ +source "null" "example" { + communicator = "none" +} + +data "hcp-packer-iteration" "hardened-source" { + bucket_name = "hardened-ubuntu-16-04" + channel = "packer-acc-test" +} + +data "hcp-packer-image" "aws" { + bucket_name = "hardened-ubuntu-16-04" + iteration_id = "${data.hcp-packer-iteration.hardened-source.id}" + cloud_provider = "aws" + region = "us-east-1" +} + +locals { + foo = "${data.hcp-packer-iteration.hardened-source.id}" + bar = "${data.hcp-packer-image.aws.id}" +} + +build { + name = "mybuild" + sources = [ + "source.null.example" + ] + provisioner "shell-local" { + inline = [ + "echo data is ${local.foo}", + "echo data is ${local.bar}" + ] + } +} diff --git a/go.mod b/go.mod index 9bdda24ab..906bd4765 100644 --- a/go.mod +++ b/go.mod @@ -10,43 +10,30 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/Telmate/proxmox-api-go v0.0.0-20210825163308-5e4c0d698a78 // indirect - github.com/agext/levenshtein v1.2.3 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.61.1244 // indirect github.com/aliyun/aliyun-oss-go-sdk v2.1.10+incompatible // indirect - github.com/armon/go-metrics v0.3.9 // indirect - github.com/aws/aws-sdk-go v1.40.34 // indirect github.com/biogo/hts v1.4.3 - github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cheggaaa/pb v1.0.27 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/digitalocean/go-libvirt v0.0.0-20210723161134-761cfeeb5968 // indirect github.com/dsnet/compress v0.0.1 - github.com/fatih/color v1.12.0 // indirect github.com/go-git/go-git/v5 v5.4.2 github.com/go-openapi/runtime v0.19.24 github.com/gobwas/glob v0.2.3 github.com/gofrs/flock v0.8.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.6 github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69 github.com/google/go-querystring v1.1.0 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 - github.com/hashicorp/consul/api v1.10.1 // indirect github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 github.com/hashicorp/go-getter/v2 v2.0.0 - github.com/hashicorp/go-hclog v0.16.2 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 - github.com/hashicorp/go-retryablehttp v0.7.0 // indirect github.com/hashicorp/go-uuid v1.0.2 github.com/hashicorp/go-version v1.3.0 - github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl/v2 v2.10.1 github.com/hashicorp/hcp-sdk-go v0.10.1-0.20210727200019-239ce8d80646 github.com/hashicorp/packer-plugin-alicloud v1.0.0 @@ -88,20 +75,16 @@ require ( github.com/hashicorp/packer-plugin-vmware v1.0.3 github.com/hashicorp/packer-plugin-vsphere v1.0.1 github.com/hashicorp/packer-plugin-yandex v1.0.2 - github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 // indirect github.com/hetznercloud/hcloud-go v1.32.0 // indirect github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 github.com/klauspost/compress v1.13.5 // indirect github.com/klauspost/pgzip v1.2.5 github.com/masterzen/winrm v0.0.0-20210623064412-3b76017826b0 - github.com/mattn/go-isatty v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08 github.com/mitchellh/cli v1.1.0 github.com/mitchellh/go-fs v0.0.0-20180402235330-b7b9ca407fff // indirect github.com/mitchellh/go-homedir v1.1.0 - github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.4.1 github.com/mitchellh/panicwrap v1.0.0 github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784 @@ -128,134 +111,14 @@ require ( golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect golang.org/x/tools v0.1.5 google.golang.org/api v0.56.0 // indirect - google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 // indirect google.golang.org/grpc v1.40.0 - gopkg.in/square/go-jose.v2 v2.6.0 // indirect ) require ( cloud.google.com/go v0.94.0 // indirect - github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1 // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 // indirect - github.com/Microsoft/go-winio v0.4.16 // indirect github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.2.0 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/acomagu/bufpipe v1.0.3 // indirect - github.com/antihax/optional v1.0.0 // indirect - github.com/apparentlymart/go-cidr v1.0.1 // indirect - github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect - github.com/armon/go-radix v1.0.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect - github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/bmatcuk/doublestar v1.1.5 // indirect - github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect - github.com/digitalocean/go-qemu v0.0.0-20210326154740-ac9e0b687001 // indirect - github.com/digitalocean/godo v1.65.0 // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/dylanmei/iso8601 v0.1.0 // indirect - github.com/emirpasic/gods v1.12.0 // indirect - github.com/ghodss/yaml v1.0.0 // indirect - github.com/go-git/gcfg v1.5.0 // indirect - github.com/go-git/go-billy/v5 v5.3.1 // indirect - github.com/go-ole/go-ole v1.2.5 // indirect - github.com/go-openapi/analysis v0.20.0 // indirect - github.com/go-openapi/errors v0.19.9 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/loads v0.20.2 // indirect - github.com/go-openapi/spec v0.20.3 // indirect - github.com/go-openapi/strfmt v0.20.0 // indirect - github.com/go-openapi/swag v0.19.14 // indirect - github.com/go-openapi/validate v0.20.2 // indirect - github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect - github.com/go-resty/resty/v2 v2.6.0 // indirect - github.com/go-stack/stack v1.8.0 // indirect - github.com/gofrs/uuid v4.0.0+incompatible // indirect - github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect - github.com/golang-jwt/jwt/v4 v4.0.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/googleapis/gax-go/v2 v2.1.0 // indirect - github.com/gophercloud/gophercloud v0.12.0 // indirect - github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c // indirect - github.com/hashicorp/aws-sdk-go-base v0.7.1 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-azure-helpers v0.16.5 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-getter/gcs/v2 v2.0.0-20200604122502-a6995fa1edad // indirect - github.com/hashicorp/go-getter/s3/v2 v2.0.0-20200604122502-a6995fa1edad // indirect - github.com/hashicorp/go-oracle-terraform v0.17.0 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-safetemp v1.0.0 // indirect - github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/serf v0.9.5 // indirect - github.com/hashicorp/vault/api v1.1.1 // indirect - github.com/hashicorp/vault/sdk v0.2.1 // indirect - github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4 // indirect - github.com/imdario/mergo v0.3.12 // indirect - github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/joyent/triton-go v1.8.5 // indirect - github.com/json-iterator/go v1.1.11 // indirect - github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect - github.com/kr/fs v0.1.0 // indirect - github.com/linode/linodego v0.28.5 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed // indirect - github.com/mitchellh/iochan v1.0.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/oracle/oci-go-sdk/v36 v36.2.0 // indirect - github.com/outscale/osc-sdk-go/osc v0.0.0-20210316122053-4dfd64ce707a // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible // indirect - github.com/prometheus/client_golang v1.11.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7 // indirect - github.com/sergi/go-diff v1.1.0 // indirect - github.com/sirupsen/logrus v1.6.0 // indirect - github.com/ucloud/ucloud-sdk-go v0.20.2 // indirect - github.com/ufilesdk-dev/ufile-gosdk v1.0.1 // indirect - github.com/ugorji/go/codec v1.2.6 // indirect - github.com/xanzy/go-cloudstack v2.4.1+incompatible // indirect - github.com/xanzy/ssh-agent v0.3.0 // indirect - github.com/yandex-cloud/go-genproto v0.0.0-20210824140926-1bca7bc0c005 // indirect - go.mongodb.org/mongo-driver v1.4.6 // indirect - go.opencensus.io v0.23.0 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.27.1 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect - gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) go 1.17 diff --git a/internal/packer_registry/registry_service.go b/internal/packer_registry/registry_service.go index deb926683..8db22979c 100644 --- a/internal/packer_registry/registry_service.go +++ b/internal/packer_registry/registry_service.go @@ -169,3 +169,22 @@ func GetIterationFromChannel(ctx context.Context, client *Client, bucketSlug str return nil, fmt.Errorf("there is no channel with the name %s associated with the bucket %s", channelName, bucketSlug) } + +// GetIteration queries the HCP Packer registry for an existing bucket iteration. +func GetIterationFromId(ctx context.Context, client *Client, bucketslug string, iterationId string) (*models.HashicorpCloudPackerIteration, error) { + params := packerSvc.NewGetIterationParamsWithContext(ctx) + params.LocationOrganizationID = client.OrganizationID + params.LocationProjectID = client.ProjectID + params.BucketSlug = bucketslug + + // The identifier can be either fingerprint, iterationid, or incremental version + // for now, we only care about fingerprint so we're hardcoding it. + params.IterationID = &iterationId + + it, err := client.Packer.GetIteration(params, nil) + if err != nil { + return nil, err + } + + return it.Payload.Iteration, nil +} diff --git a/website/content/partials/datasource/hcp-packer-image/Config-required.mdx b/website/content/partials/datasource/hcp-packer-image/Config-required.mdx new file mode 100644 index 000000000..64c3343c2 --- /dev/null +++ b/website/content/partials/datasource/hcp-packer-image/Config-required.mdx @@ -0,0 +1,12 @@ + + +- `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". + +- `region` (string) - The name of the cloud region your image is in. For example "us-east-1". + + diff --git a/website/content/partials/datasource/hcp-packer-image/DatasourceOutput.mdx b/website/content/partials/datasource/hcp-packer-image/DatasourceOutput.mdx new file mode 100644 index 000000000..2d7974fa7 --- /dev/null +++ b/website/content/partials/datasource/hcp-packer-image/DatasourceOutput.mdx @@ -0,0 +1,26 @@ + + +- `cloud_provider` (string) - The name of the cloud provider that the image exists in. For example, + "aws", "azure", or "gce". + +- `component_type` (string) - The specific Packer builder or post-processor used to create the image. + +- `created_at` (string) - The date and time at which the image was created. + +- `build_id` (string) - The id of the build that created the image. This is a ULID, which is a + unique identifier similar to a UUID. It is created by the HCP Packer + Registry when an build is first created, and is unique to this build. + +- `iteration_id` (string) - The iteration id. This is a ULID, which is a unique identifier similar + to a UUID. It is created by the HCP Packer Registry when an iteration is + first created, and is unique to this iteration. + +- `packer_run_uuid` (string) - The UUID associated with the Packer run that created this image. + +- `id` (string) - ID or URL of the remote cloud image as given by a build. + +- `region` (string) - The cloud region as given by `packer build`. eg. "ap-east-1". + For locally managed clouds, this may map instead to a cluster, server + or datastore. + + diff --git a/website/content/partials/datasource/hcp-packer-iteration/Config-required.mdx b/website/content/partials/datasource/hcp-packer-iteration/Config-required.mdx new file mode 100644 index 000000000..582bb8290 --- /dev/null +++ b/website/content/partials/datasource/hcp-packer-iteration/Config-required.mdx @@ -0,0 +1,7 @@ + + +- `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 + + diff --git a/website/content/partials/datasource/hcp-packer-iteration/DatasourceOutput.mdx b/website/content/partials/datasource/hcp-packer-iteration/DatasourceOutput.mdx new file mode 100644 index 000000000..7e8050f3c --- /dev/null +++ b/website/content/partials/datasource/hcp-packer-iteration/DatasourceOutput.mdx @@ -0,0 +1,30 @@ + + +- `author_id` (string) - who created the iteration + +- `bucket_name` (string) - Name of the bucket that the iteration was retrieved from + +- `complete` (bool) - If true, this iteration is considered "ready to use" and will be + returned even if the include_incomplete flag is "false" in the + list iterations request. Note that if you are retrieving an iteration + using a channel, this will always be "true"; channels cannot be assigned + to incomplete iterations. + +- `created_at` (string) - The date the iteration was created. + +- `fingerprint` (string) - The fingerprint of the build; this could be a git sha or other unique + identifier as set by the Packer build that created this iteration. + +- `id` (string) - The iteration id. This is a ULID, which is a unique identifier similar + to a UUID. It is created by the HCP Packer Registry when an iteration is + first created, and is unique to this iteration. + +- `incremental_version` (int32) - The version number assigned to an iteration. This number is an integer, + and is created by the HCP Packer Registry once an iteration is + marked "complete". If a new iteration is marked "complete", the version + that HCP Packer assigns to it will always be the highest previous + iteration version plus one. + +- `updated_at` (string) - The date when this iteration was last updated. + +