Skip to content

Commit

Permalink
hcp-packer-image: add support for channel
Browse files Browse the repository at this point in the history
In addition to the current way of specifying an image based on an
iteration on HCP Packer, which requires first declaring an iteration,
and then referencing it from the image to build, we add the capacity of
specifying a channel.

This alternative will get the iteration linked to the channel, and is
essentially a more convenient way to get an image's metadata from HCP
Packer.

This commit is essentially a backport from the Terraform HCP provider,
for consistency between the two tools.
  • Loading branch information
lbajolet-hashicorp committed Jul 6, 2022
1 parent 0a41694 commit d73699c
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 12 deletions.
81 changes: 72 additions & 9 deletions datasource/hcp-packer-image/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ package hcp_packer_image

import (
"context"
"errors"
"fmt"
"log"
"time"

"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"
)

Expand All @@ -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"`
Expand All @@ -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."))
Expand Down Expand Up @@ -109,6 +133,46 @@ 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 == "" &&
d.config.Channel == "" {
return nil, fmt.Errorf(
"`channel` and `iteration_id` cannot be unset at the same time for one image")
}

if d.config.IterationID != "" &&
d.config.Channel != "" {
return nil, fmt.Errorf(
"`channel` and `iteration_id` cannot be set at the same time for one image")
}

var iter *models.HashicorpCloudPackerIteration
var err 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
}

ch, 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 ch, nil
}

func (d *Datasource) Execute() (cty.Value, error) {
ctx := context.TODO()

Expand All @@ -121,10 +185,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)
Expand Down
4 changes: 3 additions & 1 deletion datasource/hcp-packer-image/data.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!-- Code generated from the comments of the Config struct in datasource/hcp-packer-image/data.go; DO NOT EDIT MANUALLY -->

- `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`

<!-- End of code generated from the comments of the Config struct in datasource/hcp-packer-image/data.go; -->
Original file line number Diff line number Diff line change
Expand Up @@ -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".

Expand Down

0 comments on commit d73699c

Please sign in to comment.