Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hcp: give a proper error when using conflicting build name #13300

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 87 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,96 @@ 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", buildName),
})
}
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