mirror of
https://github.com/hashicorp/packer.git
synced 2026-09-20 15:01:40 -04:00
* registry: add heartbeat call for running builds When a build is running, we send periodic heartbeats to HCP Packer's API. This will be used on the service side to detect if a build is stalled on the core side because of a crash or any other malfunction that caused Packer not to send an update on the status of a build. * registry: only update status on status update Prior to this commit, we'd send updates to both the labels and the cloud-provider whenever an update to the status of a build would be sent. This would cause a bug in which once a build reached the post-processing state, and its cloud-provider was set, the status could not be updated anymore, as the cloud-provider would be set and further updates are rejected by the platform. To avoid this problem, we only transmit the status when doing a status update, and no other fields along with it. * internal: publicise CompleteBuild function The markBuildComplete private function used to be called from a final status update to DONE, through the UpdateBuildStatus function. The problem being that the (now) CompleteBuild function not only updates the status of the build, but also all its metadata. For consistency, we remove the indirection, and explicitely call CompleteBuild when we want to finish a build. * internal: block status updates on DONE builds
107 lines
3.5 KiB
Go
107 lines
3.5 KiB
Go
package packer
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/hashicorp/hcl/v2/hcldec"
|
|
"github.com/hashicorp/hcp-sdk-go/clients/cloud-packer-service/stable/2021-04-30/models"
|
|
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
registryimage "github.com/hashicorp/packer-plugin-sdk/packer/registry/image"
|
|
packerregistry "github.com/hashicorp/packer/internal/registry"
|
|
"github.com/mitchellh/mapstructure"
|
|
)
|
|
|
|
type RegistryPostProcessor struct {
|
|
BuilderType string
|
|
ArtifactMetadataPublisher *packerregistry.Bucket
|
|
packersdk.PostProcessor
|
|
}
|
|
|
|
func (p *RegistryPostProcessor) ConfigSpec() hcldec.ObjectSpec {
|
|
if p.PostProcessor == nil {
|
|
return nil
|
|
}
|
|
|
|
return p.PostProcessor.ConfigSpec()
|
|
}
|
|
|
|
func (p *RegistryPostProcessor) Configure(raws ...interface{}) error {
|
|
if p.PostProcessor == nil {
|
|
return nil
|
|
}
|
|
|
|
return p.PostProcessor.Configure(raws...)
|
|
}
|
|
|
|
func (p *RegistryPostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, source packersdk.Artifact) (packersdk.Artifact, bool, bool, error) {
|
|
// This is a bit of a hack for now to denote that this pp should just update the state of a build in the Packer registry.
|
|
// TODO create an actual post-processor that we can embed here that will do the updating and printing.
|
|
if p.PostProcessor == nil {
|
|
if parErr := p.ArtifactMetadataPublisher.CompleteBuild(ctx, p.BuilderType); parErr != nil {
|
|
err := fmt.Errorf("[TRACE] failed to update Packer registry with image artifacts for %q: %s", p.BuilderType, parErr)
|
|
return nil, false, true, err
|
|
}
|
|
|
|
r := &RegistryArtifact{
|
|
BuildName: p.BuilderType,
|
|
BucketSlug: p.ArtifactMetadataPublisher.Slug,
|
|
IterationID: p.ArtifactMetadataPublisher.Iteration.ID,
|
|
}
|
|
|
|
return r, true, false, nil
|
|
}
|
|
|
|
// Bump build status first so we don't end-up chaining post-processors
|
|
// that don't heartbeat, hence letting too long happen between two
|
|
// refreshes, and letting the build go to the FAILED status.
|
|
err := p.ArtifactMetadataPublisher.UpdateBuildStatus(
|
|
ctx,
|
|
p.BuilderType,
|
|
models.HashicorpCloudPackerBuildStatusRUNNING,
|
|
)
|
|
if err != nil {
|
|
log.Printf("[TRACE] failed to heartbeat running build %s: %s", p.BuilderType, err)
|
|
}
|
|
|
|
cleanupHeartbeat, err := p.ArtifactMetadataPublisher.HeartbeatBuild(ctx, p.BuilderType)
|
|
if err != nil {
|
|
log.Printf("[ERROR] failed to start heartbeat function")
|
|
}
|
|
if cleanupHeartbeat != nil {
|
|
defer cleanupHeartbeat()
|
|
}
|
|
|
|
source, keep, override, err := p.PostProcessor.PostProcess(ctx, ui, source)
|
|
if err != nil {
|
|
if parErr := p.ArtifactMetadataPublisher.UpdateBuildStatus(ctx, p.BuilderType, models.HashicorpCloudPackerBuildStatusFAILED); parErr != nil {
|
|
log.Printf("[TRACE] failed to update Packer registry with image artifacts for %q: %s", p.BuilderType, parErr)
|
|
}
|
|
return source, false, false, err
|
|
}
|
|
|
|
var images []registryimage.Image
|
|
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
|
Result: &images,
|
|
WeaklyTypedInput: true,
|
|
ErrorUnused: false,
|
|
})
|
|
if err != nil {
|
|
return source, false, false, fmt.Errorf("failed to create decoder for HCP Packer registry image: %w", err)
|
|
}
|
|
|
|
state := source.State(registryimage.ArtifactStateURI)
|
|
err = decoder.Decode(state)
|
|
if err != nil {
|
|
return source, false, false, fmt.Errorf("failed to obtain HCP Packer registry image from post-processor artifact: %w", err)
|
|
}
|
|
err = p.ArtifactMetadataPublisher.UpdateImageForBuild(p.BuilderType, images...)
|
|
|
|
if err != nil {
|
|
return source, keep, override, fmt.Errorf("[TRACE] failed to add image artifact for %q: %s", p.BuilderType, err)
|
|
}
|
|
|
|
return source, keep, override, nil
|
|
}
|