Skip to content

Commit

Permalink
Migrate HCP Packer to API V2 and new nomenclature changes (#12794)
Browse files Browse the repository at this point in the history
* add hcp packer new nomenclature updates

* Update docs with nomenclature changes

* Update navigation bar links

* Bump github.com/hashicorp/hcp-sdk-go from 0.81.0 to 0.82.0

* fix acceptance test template

---------

Co-authored-by: sylviamoss <[email protected]>
Co-authored-by: Wilken Rivera <[email protected]>
  • Loading branch information
3 people committed Jan 24, 2024
1 parent 87b5f56 commit e837b79
Show file tree
Hide file tree
Showing 55 changed files with 2,651 additions and 1,207 deletions.
6 changes: 3 additions & 3 deletions command/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,13 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, cla *BuildArgs) int
return ret
}

defer hcpRegistry.IterationStatusSummary()
defer hcpRegistry.VersionStatusSummary()

err := hcpRegistry.PopulateIteration(buildCtx)
err := hcpRegistry.PopulateVersion(buildCtx)
if err != nil {
return writeDiags(c.Ui, nil, hcl.Diagnostics{
&hcl.Diagnostic{
Summary: "HCP: populating iteration failed",
Summary: "HCP: populating version failed",
Severity: hcl.DiagError,
Detail: err.Error(),
},
Expand Down
4 changes: 4 additions & 0 deletions command/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import (

filebuilder "github.com/hashicorp/packer/builder/file"
nullbuilder "github.com/hashicorp/packer/builder/null"
hcppackerartifactdatasource "github.com/hashicorp/packer/datasource/hcp-packer-artifact"
hcppackerimagedatasource "github.com/hashicorp/packer/datasource/hcp-packer-image"
hcppackeriterationdatasource "github.com/hashicorp/packer/datasource/hcp-packer-iteration"
hcppackerversiondatasource "github.com/hashicorp/packer/datasource/hcp-packer-version"
httpdatasource "github.com/hashicorp/packer/datasource/http"
nulldatasource "github.com/hashicorp/packer/datasource/null"
artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice"
Expand Down Expand Up @@ -63,8 +65,10 @@ var PostProcessors = map[string]packersdk.PostProcessor{
}

var Datasources = map[string]packersdk.Datasource{
"hcp-packer-artifact": new(hcppackerartifactdatasource.Datasource),
"hcp-packer-image": new(hcppackerimagedatasource.Datasource),
"hcp-packer-iteration": new(hcppackeriterationdatasource.Datasource),
"hcp-packer-version": new(hcppackerversiondatasource.Datasource),
"http": new(httpdatasource.Datasource),
"null": new(nulldatasource.Datasource),
}
Expand Down
251 changes: 251 additions & 0 deletions datasource/hcp-packer-artifact/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

//go:generate packer-sdc struct-markdown
//go:generate packer-sdc mapstructure-to-hcl2 -type DatasourceOutput,Config
package hcp_packer_artifact

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

"github.com/zclconf/go-cty/cty"

"github.com/hashicorp/hcl/v2/hcldec"
hcpPackerModels "github.com/hashicorp/hcp-sdk-go/clients/cloud-packer-service/stable/2023-01-01/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"
hcpapi "github.com/hashicorp/packer/internal/hcp/api"
)

type Datasource struct {
config Config
}

type Config struct {
common.PackerConfig `mapstructure:",squash"`

// The name of the bucket your artifact is in.
BucketName string `mapstructure:"bucket_name" required:"true"`

// The name of the channel to use when retrieving your artifact.
// Either `channel_name` or `version_fingerprint` MUST be set.
// If using several artifacts from a single version, you may prefer sourcing a version first,
// and referencing it for subsequent uses, as every `hcp_packer_artifact` with the channel set will generate a
// potentially billable HCP Packer request, but if several `hcp_packer_artifact`s use a shared `hcp_packer_version`
// that will only generate one potentially billable request.
ChannelName string `mapstructure:"channel_name" required:"true"`

// The fingerprint of the version to use when retrieving your artifact.
// Either this or `channel_name` MUST be set.
// Mutually exclusive with `channel_name`
VersionFingerprint string `mapstructure:"version_fingerprint" required:"true"`

// The name of the platform that your artifact is for.
// For example, "aws", "azure", or "gce".
Platform string `mapstructure:"platform" required:"true"`

// The name of the region your artifact is in.
// For example "us-east-1".
Region string `mapstructure:"region" required:"true"`

// The specific Packer builder used to create the artifact.
// For example, "amazon-ebs.example"
ComponentType string `mapstructure:"component_type" required:"false"`
}

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.BucketName == "" {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("the `bucket_name` must be specified"),
)
}

// Ensure either channel_name or version_fingerprint is set, and not both at the same time.
if d.config.ChannelName == "" && d.config.VersionFingerprint == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New(
"`version_fingerprint` or `channel_name` must be specified",
))
}
if d.config.ChannelName != "" && d.config.VersionFingerprint != "" {
errs = packersdk.MultiErrorAppend(errs, errors.New(
"`version_fingerprint` and `channel_name` cannot be specified together",
))
}

if d.config.Region == "" {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("the `region` must be specified"),
)
}

if d.config.Platform == "" {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf(
"the `platform` must be specified",
))
}

if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
}

// DatasourceOutput Information from []*hcpPackerModels.HashicorpCloudPacker20230101Artifact with some information
// from the parent []*hcpPackerModels.HashicorpCloudPacker20230101Build included where it seemed
// like it might be relevant. Need to copy so we can generate
type DatasourceOutput struct {
// The name of the platform that the artifact exists in.
// For example, "aws", "azure", or "gce".
Platform string `mapstructure:"platform"`

// The specific Packer builder or post-processor used to create the artifact.
ComponentType string `mapstructure:"component_type"`

// The date and time at which the artifact was created.
CreatedAt string `mapstructure:"created_at"`

// The ID of the build that created the artifact. This is a ULID, which is a
// unique identifier similar to a UUID. It is created by the HCP Packer
// Registry when a build is first created, and is unique to this build.
BuildID string `mapstructure:"build_id"`

// The version ID. This is a ULID, which is a unique identifier similar
// to a UUID. It is created by the HCP Packer Registry when a version is
// first created, and is unique to this version.
VersionID string `mapstructure:"version_id"`

// The ID of the channel used to query the version. This value will be empty if the `version_fingerprint` was used
// directly instead of a channel.
ChannelID string `mapstructure:"channel_id"`

// The UUID associated with the Packer run that created this artifact.
PackerRunUUID string `mapstructure:"packer_run_uuid"`

// Identifier or URL of the remote artifact as given by a build.
// For example, ami-12345.
ExternalIdentifier string `mapstructure:"external_identifier"`

// The 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"`

// The key:value metadata labels associated with this build.
Labels map[string]string `mapstructure:"labels"`
}

func (d *Datasource) OutputSpec() hcldec.ObjectSpec {
return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec()
}

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

cli, err := hcpapi.NewClient()
if err != nil {
return cty.NullVal(cty.EmptyObject), err
}

var version *hcpPackerModels.HashicorpCloudPacker20230101Version
var channelID string
if d.config.VersionFingerprint != "" {
log.Printf(
"[INFO] Reading info from HCP Packer Registry (%s) "+
"[project_id=%s, organization_id=%s, version_fingerprint=%s]",
d.config.BucketName, cli.ProjectID, cli.OrganizationID, d.config.VersionFingerprint,
)

version, err = cli.GetVersion(ctx, d.config.BucketName, d.config.VersionFingerprint)
if err != nil {
return cty.NullVal(cty.EmptyObject), fmt.Errorf(
"error retrieving version from HCP Packer Registry: %s", err,
)
}
} else {
log.Printf(
"[INFO] Reading info from HCP Packer Registry (%s) "+
"[project_id=%s, organization_id=%s, channel=%s]",
d.config.BucketName, cli.ProjectID, cli.OrganizationID, d.config.ChannelName,
)

var channel *hcpPackerModels.HashicorpCloudPacker20230101Channel
channel, err = cli.GetChannel(ctx, d.config.BucketName, d.config.ChannelName)
if err != nil {
return cty.NullVal(cty.EmptyObject), fmt.Errorf(
"error retrieving channel from HCP Packer Registry: %s", err.Error(),
)
}

if channel.Version == nil {
return cty.NullVal(cty.EmptyObject), fmt.Errorf(
"there is no version associated with the channel %s", d.config.ChannelName,
)
}
channelID = channel.ID
version = channel.Version
}

if *version.Status == hcpPackerModels.HashicorpCloudPacker20230101VersionStatusVERSIONREVOKED {
return cty.NullVal(cty.EmptyObject), fmt.Errorf(
"the version %s is revoked and can not be used on Packer builds", version.ID,
)
}

var output DatasourceOutput

cloudAndRegions := map[string][]string{}
for _, build := range version.Builds {
if build.Platform != d.config.Platform {
continue
}
for _, artifact := range build.Artifacts {
cloudAndRegions[build.Platform] = append(cloudAndRegions[build.Platform], artifact.Region)
if artifact.Region == d.config.Region && filterBuildByComponentType(build, d.config.ComponentType) {
// This is the desired artifact.
output = DatasourceOutput{
Platform: build.Platform,
ComponentType: build.ComponentType,
CreatedAt: artifact.CreatedAt.String(),
BuildID: build.ID,
VersionID: build.VersionID,
ChannelID: channelID,
PackerRunUUID: build.PackerRunUUID,
ExternalIdentifier: artifact.ExternalIdentifier,
Region: artifact.Region,
Labels: build.Labels,
}
return hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()), nil
}
}
}

return cty.NullVal(cty.EmptyObject), fmt.Errorf(
"could not find a build result matching "+
"[region=%q, platform=%q, component_type=%q]. Available: %v ",
d.config.Region, d.config.Platform, d.config.ComponentType, cloudAndRegions,
)
}

func filterBuildByComponentType(build *hcpPackerModels.HashicorpCloudPacker20230101Build, componentType string) bool {
// optional field is not specified, passthrough
if componentType == "" {
return true
}
// if specified, only the matched artifact metadata is returned by this effect
return build.ComponentType == componentType
}
98 changes: 98 additions & 0 deletions datasource/hcp-packer-artifact/data.hcl2spec.go

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

Loading

0 comments on commit e837b79

Please sign in to comment.