-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate HCP Packer to API V2 and new nomenclature changes (#12794)
* 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
1 parent
87b5f56
commit e837b79
Showing
55 changed files
with
2,651 additions
and
1,207 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.