mirror of https://github.com/hashicorp/packer
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
7.1 KiB
286 lines
7.1 KiB
package command
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
imgds "github.com/hashicorp/packer/datasource/hcp-packer-image"
|
|
iterds "github.com/hashicorp/packer/datasource/hcp-packer-iteration"
|
|
"github.com/hashicorp/packer/hcl2template"
|
|
"github.com/hashicorp/packer/internal/registry"
|
|
"github.com/hashicorp/packer/internal/registry/env"
|
|
"github.com/hashicorp/packer/packer"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/gocty"
|
|
)
|
|
|
|
const (
|
|
// Known HCP Packer Image Datasource, whose id is the SourceImageId for some build.
|
|
hcpImageDatasourceType string = "hcp-packer-image"
|
|
hcpIterationDatasourceType string = "hcp-packer-iteration"
|
|
buildLabel string = "build"
|
|
)
|
|
|
|
// TrySetupHCP attempts to setup the HCP-related structures if HCP is enabled
|
|
// for the command
|
|
func TrySetupHCP(cfg packer.Handler) hcl.Diagnostics {
|
|
switch cfg := cfg.(type) {
|
|
case *hcl2template.PackerConfig:
|
|
return setupRegistryForPackerConfig(cfg)
|
|
case *CoreWrapper:
|
|
return setupRegistryForPackerCore(cfg)
|
|
}
|
|
|
|
return hcl.Diagnostics{
|
|
&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "unknown Handler type",
|
|
Detail: "SetupRegistry called with an unknown Handler. " +
|
|
"This is a Packer bug and should be brought to the attention " +
|
|
"of the Packer team, please consider opening an issue for this.",
|
|
},
|
|
}
|
|
}
|
|
|
|
func setupRegistryForPackerConfig(pc *hcl2template.PackerConfig) hcl.Diagnostics {
|
|
var diags hcl.Diagnostics
|
|
|
|
hasHCP := false
|
|
|
|
for _, build := range pc.Builds {
|
|
if build.HCPPackerRegistry != nil {
|
|
hasHCP = true
|
|
}
|
|
}
|
|
|
|
if hasHCP && len(pc.Builds) > 1 {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Multiple " + buildLabel + " blocks",
|
|
Detail: fmt.Sprintf("For Packer Registry enabled builds, only one " + buildLabel +
|
|
" block can be defined. Please remove any additional " + buildLabel +
|
|
" block(s). If this " + buildLabel + " is not meant for the Packer registry please " +
|
|
"clear any HCP_PACKER_* environment variables."),
|
|
})
|
|
return diags
|
|
}
|
|
|
|
if env.IsPAREnabled() {
|
|
hasHCP = true
|
|
}
|
|
|
|
if !hasHCP {
|
|
return diags
|
|
}
|
|
|
|
var err error
|
|
pc.Bucket, err = registry.NewBucketWithIteration(registry.IterationOptions{
|
|
TemplateBaseDir: pc.Basedir,
|
|
})
|
|
|
|
if err != nil {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Summary: "Unable to create a valid bucket object for HCP Packer Registry",
|
|
Detail: fmt.Sprintf("%s", err),
|
|
Severity: hcl.DiagError,
|
|
})
|
|
|
|
return diags
|
|
}
|
|
|
|
pc.Bucket.LoadDefaultSettingsFromEnv()
|
|
|
|
for _, build := range pc.Builds {
|
|
build.HCPPackerRegistry.WriteToBucketConfig(pc.Bucket)
|
|
|
|
// If at this point the bucket.Slug is still empty,
|
|
// last try is to use the build.Name if present
|
|
if pc.Bucket.Slug == "" && build.Name != "" {
|
|
pc.Bucket.Slug = build.Name
|
|
}
|
|
|
|
// If the description is empty, use the one from the build block
|
|
if pc.Bucket.Description == "" {
|
|
pc.Bucket.Description = build.Description
|
|
}
|
|
|
|
for _, source := range build.Sources {
|
|
pc.Bucket.RegisterBuildForComponent(source.String())
|
|
}
|
|
}
|
|
|
|
vals, diags := pc.Datasources.Values()
|
|
if diags != nil {
|
|
return diags
|
|
}
|
|
|
|
imageDS, imageOK := vals[hcpImageDatasourceType]
|
|
iterDS, iterOK := vals[hcpIterationDatasourceType]
|
|
|
|
// If we don't have any image or iteration defined, we can return directly
|
|
if !imageOK && !iterOK {
|
|
return nil
|
|
}
|
|
|
|
iterations := map[string]iterds.DatasourceOutput{}
|
|
|
|
if iterOK {
|
|
hcpData := map[string]cty.Value{}
|
|
err = gocty.FromCtyValue(iterDS, &hcpData)
|
|
if err != nil {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid HCP datasources",
|
|
Detail: fmt.Sprintf("Failed to decode hcp_packer_iteration datasources: %s", err),
|
|
})
|
|
return diags
|
|
}
|
|
|
|
for k, v := range hcpData {
|
|
iterVals := v.AsValueMap()
|
|
dso := iterValueToDSOutput(iterVals)
|
|
iterations[k] = dso
|
|
}
|
|
}
|
|
|
|
images := map[string]imgds.DatasourceOutput{}
|
|
|
|
if imageOK {
|
|
hcpData := map[string]cty.Value{}
|
|
err = gocty.FromCtyValue(imageDS, &hcpData)
|
|
if err != nil {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid HCP datasources",
|
|
Detail: fmt.Sprintf("Failed to decode hcp_packer_image datasources: %s", err),
|
|
})
|
|
return diags
|
|
}
|
|
|
|
for k, v := range hcpData {
|
|
imageVals := v.AsValueMap()
|
|
dso := imageValueToDSOutput(imageVals)
|
|
images[k] = dso
|
|
}
|
|
}
|
|
|
|
for _, img := range images {
|
|
sourceIteration := registry.ParentIteration{}
|
|
|
|
sourceIteration.IterationID = img.IterationID
|
|
|
|
if img.ChannelID != "" {
|
|
sourceIteration.ChannelID = img.ChannelID
|
|
} else {
|
|
for _, it := range iterations {
|
|
if it.ID == img.IterationID {
|
|
sourceIteration.ChannelID = it.ChannelID
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
pc.Bucket.SourceImagesToParentIterations[img.ID] = sourceIteration
|
|
}
|
|
|
|
return diags
|
|
}
|
|
|
|
func imageValueToDSOutput(imageVal map[string]cty.Value) imgds.DatasourceOutput {
|
|
dso := imgds.DatasourceOutput{}
|
|
for k, v := range imageVal {
|
|
switch k {
|
|
case "id":
|
|
dso.ID = v.AsString()
|
|
case "region":
|
|
dso.Region = v.AsString()
|
|
case "labels":
|
|
labels := map[string]string{}
|
|
lbls := v.AsValueMap()
|
|
for k, v := range lbls {
|
|
labels[k] = v.AsString()
|
|
}
|
|
dso.Labels = labels
|
|
case "packer_run_uuid":
|
|
dso.PackerRunUUID = v.AsString()
|
|
case "channel_id":
|
|
dso.ChannelID = v.AsString()
|
|
case "iteration_id":
|
|
dso.IterationID = v.AsString()
|
|
case "build_id":
|
|
dso.BuildID = v.AsString()
|
|
case "created_at":
|
|
dso.CreatedAt = v.AsString()
|
|
case "component_type":
|
|
dso.ComponentType = v.AsString()
|
|
case "cloud_provider":
|
|
dso.CloudProvider = v.AsString()
|
|
}
|
|
}
|
|
|
|
return dso
|
|
}
|
|
|
|
func iterValueToDSOutput(iterVal map[string]cty.Value) iterds.DatasourceOutput {
|
|
dso := iterds.DatasourceOutput{}
|
|
for k, v := range iterVal {
|
|
switch k {
|
|
case "author_id":
|
|
dso.AuthorID = v.AsString()
|
|
case "bucket_name":
|
|
dso.BucketName = v.AsString()
|
|
case "complete":
|
|
// For all intents and purposes, cty.Value.True() acts
|
|
// like a AsBool() would.
|
|
dso.Complete = v.True()
|
|
case "created_at":
|
|
dso.CreatedAt = v.AsString()
|
|
case "fingerprint":
|
|
dso.Fingerprint = v.AsString()
|
|
case "id":
|
|
dso.ID = v.AsString()
|
|
case "incremental_version":
|
|
// Maybe when cty provides a good way to AsInt() a cty.Value
|
|
// we can consider implementing this.
|
|
case "updated_at":
|
|
dso.UpdatedAt = v.AsString()
|
|
case "channel_id":
|
|
dso.ChannelID = v.AsString()
|
|
}
|
|
}
|
|
return dso
|
|
}
|
|
|
|
func setupRegistryForPackerCore(cfg *CoreWrapper) hcl.Diagnostics {
|
|
if !env.IsPAREnabled() {
|
|
return nil
|
|
}
|
|
|
|
var diags hcl.Diagnostics
|
|
|
|
var err error
|
|
|
|
core := cfg.Core
|
|
|
|
core.Bucket, err = registry.NewBucketWithIteration(registry.IterationOptions{
|
|
TemplateBaseDir: filepath.Dir(core.Template.Path),
|
|
})
|
|
if err != nil {
|
|
return append(diags, &hcl.Diagnostic{
|
|
Summary: "bucket creation failure",
|
|
Detail: fmt.Sprintf("failed to create Bucket: %s", err),
|
|
Severity: hcl.DiagError,
|
|
})
|
|
}
|
|
core.Bucket.LoadDefaultSettingsFromEnv()
|
|
|
|
for _, b := range core.Template.Builders {
|
|
// Get all builds slated within config ignoring any only or exclude flags.
|
|
core.Bucket.RegisterBuildForComponent(b.Name)
|
|
}
|
|
|
|
return nil
|
|
}
|