implement contextual variable packer.iteration_id

pull/11319/head
Megan Marsh 4 years ago
parent 961d72bb37
commit b763b79d9f

@ -156,6 +156,29 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, cla *BuildArgs) int
return ret
}
// This build currently enforces a 1:1 mapping that one publisher can be assigned to a single packer config file.
// It also requires that each config type implements this ConfiguredArtifactMetadataPublisher to return a configured bucket.
// TODO find an option that is not managed by a globally shared Publisher.
ArtifactMetadataPublisher, diags := packerStarter.ConfiguredArtifactMetadataPublisher()
if diags.HasErrors() {
return writeDiags(c.Ui, nil, diags)
}
// We need to create a bucket and an empty iteration before we retrieve builds
// so that we can add the iteration ID to the build's eval context
if ArtifactMetadataPublisher != nil {
if err := ArtifactMetadataPublisher.Initialize(buildCtx); err != nil {
diags := hcl.Diagnostics{
&hcl.Diagnostic{
Summary: "HCP Packer Registry iteration initialization failed",
Detail: fmt.Sprintf("Failed to initialize iteration for %q\n %s", ArtifactMetadataPublisher.Slug, err),
Severity: hcl.DiagError,
},
}
return writeDiags(c.Ui, nil, diags)
}
}
builds, diags := packerStarter.GetBuilds(packer.GetBuildsOptions{
Only: cla.Only,
Except: cla.Except,
@ -172,19 +195,13 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, cla *BuildArgs) int
c.Ui.Say("Debug mode enabled. Builds will not be parallelized.")
}
// This build currently enforces a 1:1 mapping that one publisher can be assigned to a single packer config file.
// It also requires that each config type implements this ConfiguredArtifactMetadataPublisher to return a configured bucket.
// TODO find an option that is not managed by a globally shared Publisher.
ArtifactMetadataPublisher, diags := packerStarter.ConfiguredArtifactMetadataPublisher()
if diags.HasErrors() {
return writeDiags(c.Ui, nil, diags)
}
// Now that builds have been retrieved, we can populate the iteration with
// the builds we expect to run.
if ArtifactMetadataPublisher != nil {
if err := ArtifactMetadataPublisher.Initialize(buildCtx); err != nil {
if err := ArtifactMetadataPublisher.PopulateIteration(buildCtx); err != nil {
diags := hcl.Diagnostics{
&hcl.Diagnostic{
Summary: "HCP Packer Registry initialization failed",
Summary: "HCP Packer Registry build initialization failed",
Detail: fmt.Sprintf("Failed to initialize build for %q\n %s", ArtifactMetadataPublisher.Slug, err),
Severity: hcl.DiagError,
},

@ -109,8 +109,8 @@ func (cfg *PackerConfig) EvalContext(ctx BlockContext, variables map[string]cty.
}),
buildAccessor: cty.UnknownVal(cty.EmptyObject),
packerAccessor: cty.ObjectVal(map[string]cty.Value{
"version": cty.StringVal(cfg.CorePackerVersionString),
"iteration_id": cty.UnknownVal(cty.String),
"version": cty.StringVal(cfg.CorePackerVersionString),
"iterationID": cty.UnknownVal(cty.String),
}),
pathVariablesAccessor: cty.ObjectVal(map[string]cty.Value{
"cwd": cty.StringVal(strings.ReplaceAll(cfg.Cwd, `\`, `/`)),
@ -119,6 +119,14 @@ func (cfg *PackerConfig) EvalContext(ctx BlockContext, variables map[string]cty.
},
}
// Store the iteration_id, if it exists. Otherwise, it'll be "unknown"
if cfg.bucket != nil {
ectx.Variables[packerAccessor] = cty.ObjectVal(map[string]cty.Value{
"version": cty.StringVal(cfg.CorePackerVersionString),
"iterationID": cty.StringVal(cfg.bucket.Iteration.ID),
})
}
// In the future we'd like to load and execute HCL blocks using a graph
// dependency tree, so that any block can use any block whatever the
// order.

@ -289,16 +289,11 @@ func (b *Bucket) createIteration() (*models.HashicorpCloudPackerIteration, error
return iterationResp, nil
}
// initializeIteration populates the bucket iteration with the details needed for tracking builds for a Packer run.
// If an existing Packer registry iteration exists for the said iteration fingerprint, calling initialize on iteration
// that doesn't yet exist will call createIteration to create the entry on the HCP packer registry for the given bucket.
// All build details will be created (if they don't exists) and added to b.Iteration.builds for tracking during runtime.
func (b *Bucket) initializeIteration(ctx context.Context) error {
// load existing iteration using fingerprint.
iterationResp, err := GetIteration(ctx, b.client, b.Slug, b.Iteration.Fingerprint)
if checkErrorCode(err, codes.Aborted) {
//probably means Iteration doesn't exist need a way to check the error
// probably means Iteration doesn't exist need a way to check the error
iterationResp, err = b.createIteration()
}
@ -321,9 +316,17 @@ func (b *Bucket) initializeIteration(ctx context.Context) error {
"If you wish to add a new build to this image a new iteration must be created by changing the build fingerprint.", b.Iteration.Fingerprint)
}
return nil
}
// populateIteration populates the bucket iteration with the details needed for tracking builds for a Packer run.
// If an existing Packer registry iteration exists for the said iteration fingerprint, calling initialize on iteration
// that doesn't yet exist will call createIteration to create the entry on the HCP packer registry for the given bucket.
// All build details will be created (if they don't exists) and added to b.Iteration.builds for tracking during runtime.
func (b *Bucket) PopulateIteration(ctx context.Context) error {
// list all this iteration's builds so we can figure out which ones
// we want to run against. TODO: pagination?
existingBuilds, err := ListBuilds(ctx, b.client, b.Slug, iterationResp.ID)
existingBuilds, err := ListBuilds(ctx, b.client, b.Slug, b.Iteration.ID)
if err != nil {
return fmt.Errorf("error listing builds for this existing iteration: %s", err)
}

@ -42,14 +42,22 @@ func TestInitialize_NewBucketNewIteration(t *testing.T) {
t.Errorf("expected a call to CreateIteration but it didn't happen")
}
if !mockService.CreateBuildCalled {
t.Errorf("expected a call to CreateBuild but it didn't happen")
if mockService.CreateBuildCalled {
t.Errorf("Didn't expect a call to CreateBuild")
}
if b.Iteration.ID != "iteration-id" {
t.Errorf("expected an iteration to created but it didn't")
}
err = b.PopulateIteration(context.TODO())
if err != nil {
t.Errorf("unexpected failure: %v", err)
}
if !mockService.CreateBuildCalled {
t.Errorf("Expected a call to CreateBuild but it didn't happen")
}
if _, ok := b.Iteration.builds.Load("happycloud.image"); !ok {
t.Errorf("expected a basic build entry to be created but it didn't")
}
@ -91,14 +99,22 @@ func TestInitialize_ExistingBucketNewIteration(t *testing.T) {
t.Errorf("expected a call to CreateIteration but it didn't happen")
}
if !mockService.CreateBuildCalled {
t.Errorf("expected a call to CreateBuild but it didn't happen")
if mockService.CreateBuildCalled {
t.Errorf("Didn't expect a call to CreateBuild")
}
if b.Iteration.ID != "iteration-id" {
t.Errorf("expected an iteration to created but it didn't")
}
err = b.PopulateIteration(context.TODO())
if err != nil {
t.Errorf("unexpected failure: %v", err)
}
if !mockService.CreateBuildCalled {
t.Errorf("Expected a call to CreateBuild but it didn't happen")
}
if _, ok := b.Iteration.builds.Load("happycloud.image"); !ok {
t.Errorf("expected a basic build entry to be created but it didn't")
}
@ -133,6 +149,10 @@ func TestInitialize_ExistingBucketExistingIteration(t *testing.T) {
if err != nil {
t.Errorf("unexpected failure: %v", err)
}
err = b.PopulateIteration(context.TODO())
if err != nil {
t.Errorf("unexpected failure: %v", err)
}
if mockService.CreateBucketCalled {
t.Errorf("unexpected call to CreateBucket")
@ -158,6 +178,10 @@ func TestInitialize_ExistingBucketExistingIteration(t *testing.T) {
t.Errorf("expected an iteration to created but it didn't")
}
err = b.PopulateIteration(context.TODO())
if err != nil {
t.Errorf("unexpected failure: %v", err)
}
loadedBuild, ok := b.Iteration.builds.Load("happycloud.image")
if !ok {
t.Errorf("expected a basic build entry to be created but it didn't")
@ -201,11 +225,11 @@ func TestInitialize_ExistingBucketCompleteIteration(t *testing.T) {
err = b.Initialize(context.TODO())
if err == nil {
t.Errorf("Calling initialize on a completed Iteration should fail hard")
t.Errorf("unexpected failure: %v", err)
}
if mockService.CreateIterationCalled {
t.Errorf("unexpected a call to CreateIteration")
t.Errorf("unexpected call to CreateIteration")
}
if !mockService.GetIterationCalled {
@ -213,11 +237,11 @@ func TestInitialize_ExistingBucketCompleteIteration(t *testing.T) {
}
if mockService.CreateBuildCalled {
t.Errorf("unexpected a call to CreateBuild")
t.Errorf("unexpected call to CreateBuild")
}
if b.Iteration.ID != "iteration-id" {
t.Errorf("expected an iteration to returned but it didn't")
t.Errorf("expected an iteration to be returned but it wasn't")
}
}
@ -249,6 +273,10 @@ func TestUpdateBuildStatus(t *testing.T) {
if err != nil {
t.Errorf("unexpected failure: %v", err)
}
err = b.PopulateIteration(context.TODO())
if err != nil {
t.Errorf("unexpected failure: %v", err)
}
loadedBuild, ok := b.Iteration.builds.Load("happycloud.image")
if !ok {
@ -312,6 +340,10 @@ func TestUpdateBuildStatus_DONENoImages(t *testing.T) {
if err != nil {
t.Errorf("unexpected failure: %v", err)
}
err = b.PopulateIteration(context.TODO())
if err != nil {
t.Errorf("unexpected failure: %v", err)
}
loadedBuild, ok := b.Iteration.builds.Load("happycloud.image")
if !ok {

@ -129,3 +129,37 @@ null.first-example: output will be in this color.
Make sure to wrap your variable in single quotes in order to escape the
string that is returned; if you are running a dev version of packer the
parenthesis may through off your shell escaping otherwise.
# HCP Packer Iteration ID
If your build is pushing metadata to the HCP Packer reigstry, this variable is
set to the value of the Iteration ID associated with this run.
```hcl
source "amazon-ebs" "cannonical-ubuntu-server" {
ami_name = "packer-example"
// ...
run_volume_tags = {
hcp_iteration_id = packer.iterationID
}
}
```
```shell-session
==> vanilla.amazon-ebs.cannonical-ubuntu-server: Adding tags to source instance
vanilla.amazon-ebs.cannonical-ubuntu-server: Adding tag: "Name": "Packer Builder"
vanilla.amazon-ebs.cannonical-ubuntu-server: Adding tag: "hcp_iteration_id": "01FHGF3M2AK4TS6PCZES4VX5E7"
```
You can also add this value to post-processors, for example to add to a manifest file:
```hcl
post-processor "manifest" {
output = "manifest.json"
strip_path = true
custom_data = {
iteration = "${packer.iterationID}"
}
}
```
Loading…
Cancel
Save