-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Update HCP Packer build labels when re-running Packer on an incomplete build #11584
Conversation
Previously when running a partial build on multi-cloud build template it was found that build labels were only being applied at the creation for the partially executed build. Leaving all other completed builds with no HCP Packer build labels. This updates how incomplete builds are loaded from the registry and ensure that any defined hcp_packer_registry.build_labels are assigned to the build before starting an actual Packer build. Related to: #11573
Before Change ``` WARNING: DATA RACE Write at 0x00c0005421b0 by goroutine 47: github.com/hashicorp/packer/internal/registry.(*MockPackerClientService).PackerServiceCreateBuild() /Users/scrubbed/Development/packer/internal/registry/mock_service.go:173 +0x2b6 github.com/hashicorp/packer/internal/registry.(*Client).CreateBuild() /Users/scrubbed/Development/packer/internal/registry/service.go:169 +0x592 github.com/hashicorp/packer/internal/registry.(*Bucket).CreateInitialBuildForIteration() /Users/scrubbed/Development/packer/internal/registry/types.bucket.go:107 +0x204 github.com/hashicorp/packer/internal/registry.(*Bucket).PopulateIteration.func1() /Users/scrubbed/Development/packer/internal/registry/types.bucket.go:368 +0x14e github.com/hashicorp/packer/internal/registry.(*Bucket).PopulateIteration·dwrap·1() /Users/scrubbed/Development/packer/internal/registry/types.bucket.go:376 +0x58 Previous write at 0x00c0005421b0 by goroutine 46: github.com/hashicorp/packer/internal/registry.(*MockPackerClientService).PackerServiceCreateBuild() /Users/scrubbed/Development/packer/internal/registry/mock_service.go:173 +0x2b6 github.com/hashicorp/packer/internal/registry.(*Client).CreateBuild() /Users/scrubbed/Development/packer/internal/registry/service.go:169 +0x592 github.com/hashicorp/packer/internal/registry.(*Bucket).CreateInitialBuildForIteration() /Users/scrubbed/Development/packer/internal/registry/types.bucket.go:107 +0x204 github.com/hashicorp/packer/internal/registry.(*Bucket).PopulateIteration.func1() /Users/scrubbed/Development/packer/internal/registry/types.bucket.go:368 +0x14e github.com/hashicorp/packer/internal/registry.(*Bucket).PopulateIteration·dwrap·1() /Users/scrubbed/Development/packer/internal/registry/types.bucket.go:376 +0x58 Goroutine 47 (running) created at: github.com/hashicorp/packer/internal/registry.(*Bucket).PopulateIteration() /Users/scrubbed/Development/packer/internal/registry/types.bucket.go:362 +0x5c7 github.com/hashicorp/packer/internal/registry.TestBucket_UpdateLabelsForBuild_withMultipleBuilds() /Users/scrubbed/Development/packer/internal/registry/types.bucket_test.go:179 +0xf7 testing.tRunner() /usr/local/go/src/testing/testing.go:1259 +0x22f testing.(*T).Run·dwrap·21() /usr/local/go/src/testing/testing.go:1306 +0x47 Goroutine 46 (finished) created at: github.com/hashicorp/packer/internal/registry.(*Bucket).PopulateIteration() /Users/scrubbed/Development/packer/internal/registry/types.bucket.go:362 +0x5c7 github.com/hashicorp/packer/internal/registry.TestBucket_UpdateLabelsForBuild_withMultipleBuilds() /Users/scrubbed/Development/packer/internal/registry/types.bucket_test.go:179 +0xf7 testing.tRunner() /usr/local/go/src/testing/testing.go:1259 +0x22f testing.(*T).Run·dwrap·21() /usr/local/go/src/testing/testing.go:1306 +0x47 ================== ```
df8ae13
to
c80d9e0
Compare
noDiffExpected: false, | ||
}, | ||
{ | ||
desc: "existing incomplete build with extra previously set build label", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me see if I got it right, if I rerun an incomplete iteration, can I add more build labels the second time I try building it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No wait, is it the other way around? If I'm trying to complete an existing iteration, I can't update labels?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is actually the first one, but the example is a little confusing. Right now if a user sets one build label and the build fails for whatever reason the build label will get persisted. Now lets say when they rerun the failed build they decide to add a few extra build labels the new labels will get added but the old label will not get removed.
If the value for the previous label is modified the old persisted label will get updated to reflect the new value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh got it! I understood that aftewards. Makes sense!
internal/registry/types.bucket.go
Outdated
log.Printf("[TRACE] build %s already exists in Packer registry, continuing...", name) | ||
return | ||
} | ||
|
||
errs = multierror.Append(errs, err) | ||
}(buildName) | ||
wg.Wait() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did this one move inside the for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was moved to fix a race condition that is introduced by calling CreateBuild on the MockService. By spawning off multiple routines for the CreateBuild the Mock Data is overwritten before it can be read resulting in a data race. This change makes it a bit more sequential. Maybe the correct change should be to leave it as it as and add a mutex to the Mock Client to prevent overwrites.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, now it will always wait for the goroutine before proceeding with the loop. I would either remove th goroutine here or fix the mutex. 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I updated the Mock service a little to be a bit more concurrent. It is a hack TBH but it works for the testing purposes. In doing so I found a race condition with the appending of errors so that it good.
func (b *Build) IsNotDone() bool { | ||
hasBuildID := b.ID != "" | ||
hasImages := len(b.Images) == 0 | ||
isNotDone := b.Status != models.HashicorpCloudPackerBuildStatusDONE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this validation be enough?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe so. Do you think we should validate something else?
The rationale is as follows:
- A build without and Id has not been registered on the registry therefore it doesn't exist and not worth considering.
- A build that exists on the registry with no images is by the service specs not done.
- An image can only be set to the done status if it has at least one image.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I confused you. I was saying that this validation isNotDone := b.Status != models.HashicorpCloudPackerBuildStatusDONE
I expected to be enough 🤔
I'm wanted to know if you had any other use cases that this wasn't enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah got it. This might be artifact of an edge case earlier on where we may have created or put a build in a done status without having images.But I can't recall immediately. I believe the API does not allow a build to be put in the DONE status without images so that check might not be needed
I have to review previous commits and notes before looking to remove the other conditions 😄
It is a simple set of changes to make it work for the current testing use cases. If we need to we can move the Called fields to counters or re-architect the mock. This change also fixes a race condition when appending to the Slice of errs when calling PopulateIteration.
112f368
to
a3898f6
Compare
Only append to errs if err is not nil Co-authored-by: Sylvia Moss <moss@hashicorp.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
Nice one ! |
I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. |
Previously when running a partial build on multi-cloud build template it was found that build labels were only being applied at the creation for the partially executed build. Leaving all other completed builds with no HCP Packer build labels. This updates how incomplete builds are loaded from the registry and ensures that any defined hcp_packer_registry.build_labels are assigned to the build before starting an actual Packer build.
Related to: #11573