fix(datastore): prevent nil deployment metadata panic - #7229
fix(datastore): prevent nil deployment metadata panic#7229anubhavsingh2106 wants to merge 2 commits into
Conversation
✅ Deploy Preview for pipecd-site ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
| } | ||
|
|
||
| func TestUpdateSharedMetadataWithNilMetadataV2(t *testing.T) { | ||
| ctrl := gomock.NewController(t) |
There was a problem hiding this comment.
these two tests are almost identical, you could pull the deployment fixture into a helper and table-drive the two cases, similar to TestMergeMetadata / TestListDeployments above 🤔
There was a problem hiding this comment.
Thanks for pointing that out! I initially kept the tests separate to make it explicit that both UpdateSharedMetadata and UpdatePluginMetadata are covered independently. But I agree that there’s quite a bit of duplicated setup here. I’ll refactor the deployment fixture into a helper and table-drive these cases, following the existing test patterns. Thanks for the suggestion!
Signed-off-by: Anubhav Singh <anmolkfzd@gmail.com>
9e3d314 to
3a52977
Compare
|
Made the changes, pls review when u get time. |
There was a problem hiding this comment.
Pull request overview
This PR hardens the datastore deployment update paths by preventing nil-pointer panics when updating Deployment.MetadataV2 metadata, aligning behavior with the expectation that older/partially-initialized deployment records can still be updated safely.
Changes:
- Initialize
d.MetadataV2when nil inUpdateSharedMetadata()andUpdatePluginMetadata()to avoid dereferencing a nil pointer. - Add regression tests covering both update methods when
MetadataV2starts as nil.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| pkg/datastore/deploymentstore.go | Adds nil-guard initialization for MetadataV2 before updating shared/plugin metadata. |
| pkg/datastore/deploymentstore_test.go | Adds regression coverage ensuring shared/plugin metadata updates succeed when MetadataV2 is nil. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
How could this case occur, can you show me one example where metadatav2 can be nil It could help me much more if you can reproduce it @anubhavsingh2106 |
|
Yeah, I was able to reproduce this locally, @armistcxy . Before the fix, both |
|
Hi, I need real proof for this one. Since I dont think that this case might happen, you can find where the metadatav2 be passed into or created for better proof 🤔 Defensive is good but I think that too much check for nil might make the function look "verbose" |
|
@armistcxy, I dug a bit deeper into this. You're right that the current deployment creation path initializes MetadataV2, so newly created deployments shouldn't hit this. The concern is with existing deployments loaded from the datastore — Update() operates on the already-persisted Deployment, so the update methods currently assume MetadataV2 is always present. I haven't found a current creation path that explicitly produces a nil MetadataV2, so I agree the real-world reproduction isn't as clear as I initially thought. The regression test demonstrates the actual panic when the field is absent, but I don't want to overstate that this is definitely possible in current production data. If MetadataV2 is guaranteed to be present for every persisted deployment, I'm happy to reconsider the nil check and keep the change minimal. |
|
Sorry but I won't approve until you can produce a fail path |
|
@armistcxy I dug deeper into this and was able to reproduce the panic through the actual MySQL datastore, rather than just with a mocked Deployment. The flow I tested is: Create a valid Deployment without setting MetadataV2. With the original implementation, the test hits d.MetadataV2.Shared while MetadataV2 is nil and produces an actual nil pointer dereference: panic: runtime error: invalid memory address or nil pointer dereference The stack trace points directly to deploymentstore.go where MetadataV2.Shared is accessed. After adding the nil initialization, I ran the exact same integration test again and it passes. So I agree that a defensive nil check shouldn't be added without a real use case. In this case, though, the integration test demonstrates that MetadataV2 == nil is a valid persisted state and that it can reach this update path and cause a real runtime panic. |
What does this PR do?
Prevents
UpdateSharedMetadata()andUpdatePluginMetadata()frompanicking when called on a deployment with a nil
MetadataV2field.Both methods previously dereferenced
d.MetadataV2without checkingwhether it had been initialized, which could result in a nil pointer
panic.
Changes
MetadataV2when it is nil before updating metadata.Testing
go test ./pkg/datastore/... -count=1✅git diff --check✅gofmt✅Impact
Deployments with an uninitialized
MetadataV2can now safely updateshared or plugin metadata without causing a runtime panic.
Related Issue
Closes #7228