Skip to content

Commit

Permalink
hcp: give a proper error when using conflicting build name
Browse files Browse the repository at this point in the history
It was possible to put the same source 2 times in the build and when
using HCP, it would error eventually since they are considered the same
build from HCP side
  • Loading branch information
mogrogan committed Feb 25, 2025
1 parent f574090 commit 626f1c8
Show file tree
Hide file tree
Showing 2 changed files with 407 additions and 10 deletions.
98 changes: 88 additions & 10 deletions internal/hcp/registry/hcl.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type HCLRegistry struct {
bucket *Bucket
ui sdkpacker.Ui
metadata *MetadataStore
buildNames map[string]struct{}
}

const (
Expand Down Expand Up @@ -66,7 +67,7 @@ func (h *HCLRegistry) PopulateVersion(ctx context.Context) error {

// StartBuild is invoked when one build for the configuration is starting to be processed
func (h *HCLRegistry) StartBuild(ctx context.Context, build *packer.CoreBuild) error {
return h.bucket.startBuild(ctx, build.Type)
return h.bucket.startBuild(ctx, h.HCPBuildName(build))
}

// CompleteBuild is invoked when one build for the configuration has finished
Expand All @@ -76,7 +77,7 @@ func (h *HCLRegistry) CompleteBuild(
artifacts []sdkpacker.Artifact,
buildErr error,
) ([]sdkpacker.Artifact, error) {
buildName := build.Type
buildName := h.HCPBuildName(build)
buildMetadata, envMetadata := build.GetMetadata(), h.metadata
err := h.bucket.Version.AddMetadataToBuild(ctx, buildName, buildMetadata, envMetadata)
if err != nil {
Expand Down Expand Up @@ -144,20 +145,97 @@ func NewHCLRegistry(config *hcl2template.PackerConfig, ui sdkpacker.Ui) (*HCLReg
return nil, diags
}

for _, source := range build.Sources {
bucket.RegisterBuildForComponent(source.String())
}

ui.Say(fmt.Sprintf("Tracking build on HCP Packer with fingerprint %q", bucket.Version.Fingerprint))

return &HCLRegistry{
registry := &HCLRegistry{
configuration: config,
bucket: bucket,
ui: ui,
metadata: &MetadataStore{},
}, nil
buildNames: map[string]struct{}{},
}

ui.Say(fmt.Sprintf("Tracking build on HCP Packer with fingerprint %q", bucket.Version.Fingerprint))

return registry, registry.registerAllComponents()
}

func (h *HCLRegistry) registerAllComponents() hcl.Diagnostics {
var diags hcl.Diagnostics

conflictSources := map[string]struct{}{}

// we currently support only one build block but it will change in the near future
for _, build := range h.configuration.Builds {
for _, source := range build.Sources {
// If we encounter the same source twice, we'll defer
// its addition to later, using both the build name
// and the source type as the name used for HCP Packer.
_, ok := h.buildNames[source.String()]
if !ok {
h.buildNames[source.String()] = struct{}{}
continue
}

conflictSources[source.String()] = struct{}{}
// We need to delete it to avoid having a false-positive
// when returning the name, since we'll be using
// the combination
delete(h.buildNames, source.String())
}
}

// Second pass is to take care of conflicting sources
//
// If the same source is used twice in the configuration, we need to
// have a way to differentiate the two on HCP, as each build should have
// a locally unique name.
//
// If that happens, we then use a combination of both the build name, and
// the source type.
for _, build := range h.configuration.Builds {
for _, source := range build.Sources {
if _, ok := conflictSources[source.String()]; !ok {
continue
}

buildName := source.String()
if build.Name != "" {
buildName = fmt.Sprintf("%s.%s", build.Name, buildName)
}

if _, ok := h.buildNames[buildName]; ok {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Build name conflicts",
Subject: &build.HCL2Ref.DefRange,
Detail: fmt.Sprintf("Two sources are used in the same build block, causing "+
"a conflict, there must only be one instance of %s.%s",
build.Name, source.String()),
})
}
h.buildNames[buildName] = struct{}{}
}
}

if diags.HasErrors() {
return diags
}

for buildName := range h.buildNames {
h.bucket.RegisterBuildForComponent(buildName)
}
return diags
}

func (h *HCLRegistry) Metadata() Metadata {
return h.metadata
}

// HCPBuildName will return the properly formatted string taking name conflict into account
func (h *HCLRegistry) HCPBuildName(build *packer.CoreBuild) string {
_, ok := h.buildNames[build.Type]
if ok {
return build.Type
}

return fmt.Sprintf("%s.%s", build.BuildName, build.Type)
}
Loading

0 comments on commit 626f1c8

Please sign in to comment.