fix(backup): refactor backups .status.underlyingResources#2319
Conversation
📝 WalkthroughWalkthroughReplaced the typed Changes
Sequence DiagramsequenceDiagram
participant Controller as BackupJobReconciler
participant BackupAPI as BackupStatus<br/>(API Object)
participant Serializer as Runtime<br/>Serialization
participant Velero as Velero<br/>Backup System
rect rgba(100,150,200,0.5)
Note over Controller,Velero: Backup Phase
Controller->>Controller: collectUnderlyingResources()
Controller->>Controller: build vmInstanceResources (DataVolumes, IP, MAC) + TypeMeta
Controller->>Serializer: marshalUnderlyingResources() -> JSON
Serializer-->>Controller: runtime.RawExtension (opaque)
Controller->>BackupAPI: store UnderlyingResources = RawExtension
Controller->>Velero: create Velero Backup (annotation = RawExtension JSON)
end
rect rgba(150,200,150,0.5)
Note over Controller,Velero: Restore Phase
Controller->>BackupAPI: read Backup.status.UnderlyingResources (RawExtension)
Controller->>Serializer: getVMInstanceResources(RawExtension) -> vmInstanceResources
Serializer-->>Controller: VM fields (DataVolumes, IP, MAC)
Controller->>Controller: createResourceModifiersConfigMap(..., ur)
Controller->>Velero: create Velero Restore (resource modifiers)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request refactors the UnderlyingResources field in the BackupStatus from a fixed struct to a generic map of runtime.RawExtension. This change enables the backup system to store kind-specific metadata in a flexible, opaque format, with the CRD updated to preserve unknown fields. The controllers have been updated to use new helper functions for marshaling and unmarshaling these resources. Feedback was provided regarding the getVMInstanceResources helper, which currently swallows unmarshaling errors, potentially leading to silent failures during backup or restore operations.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/backupcontroller/velerostrategy_controller.go`:
- Around line 1050-1063: Call resolveUnderlyingResourcesForRestore(...) early
(before any restore-side consumers) and thread the returned ur through the
restore flow instead of reading backup.Status.UnderlyingResources directly;
update prepareForRestore(...) and createResourceModifiersConfigMap(...) to
accept the resolved ur parameter and switch their internal reads from
backup.Status.UnderlyingResources to use the passed ur, and update all callers
to pass the resolved ur so VMDisk/PVC/DataVolume prep (and the OVN patch path)
runs even when backup.Status.UnderlyingResources was pruned.
- Around line 97-111: getVMInstanceResources currently only looks for the
vmInstanceKind key and returns nil for legacy backups that store a flat map
(dataVolumes/ip/mac); update it to detect the old shape by checking for keys
"dataVolumes", "ip" or "mac" in the provided map and, when present, unmarshal
that flat payload into vmInstanceResources as a compatibility path before
returning. Specifically, inside getVMInstanceResources inspect ur for
vmInstanceKind as before, and if not found iterate/check ur for raw entries that
contain "dataVolumes"|"ip"|"mac" (or attempt unmarshalling into an intermediate
map[string]json.RawMessage) and convert/merge those fields into the
vmInstanceResources struct, returning the populated *vmInstanceResources; keep
existing behavior for normal vmInstanceKind handling and for error cases return
nil as before.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 77fe8775-831d-48c9-a5a2-a3ef0ddda3c1
📒 Files selected for processing (4)
api/backups/v1alpha1/backup_types.goapi/backups/v1alpha1/zz_generated.deepcopy.gointernal/backupcontroller/velerostrategy_controller.gopackages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml
Suggestion: simplify
|
Signed-off-by: Andrey Kolkov <[email protected]>
0a1972d to
16223dc
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/backupcontroller/velerostrategy_controller.go`:
- Around line 78-93: The marshaled vmInstanceResources must include Kubernetes
TypeMeta and decoding must validate Kind: update the vmInstanceResources struct
to embed metav1.TypeMeta, modify marshalUnderlyingResources (or a vm-specific
wrapper used there) to set TypeMeta.Kind = vmInstanceKind and
TypeMeta.APIVersion to the appropriate group/version before JSON marshalling,
and update getVMInstanceResources to check the decoded object's Kind equals
vmInstanceKind (return an error if it mismatches) while preserving backward
compatibility by accepting and decoding payloads that lack a Kind field as a
legacy fallback; use the vmInstanceResources type and vmInstanceKind constant to
locate the affected code.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 13c886c8-1abd-44dd-9eb9-d83e7ad09376
📒 Files selected for processing (4)
api/backups/v1alpha1/backup_types.goapi/backups/v1alpha1/zz_generated.deepcopy.gointernal/backupcontroller/velerostrategy_controller.gopackages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml
| // vmInstanceResources contains VM-specific underlying resources discovered during backup. | ||
| type vmInstanceResources struct { | ||
| DataVolumes []backupsv1alpha1.DataVolumeResource `json:"dataVolumes,omitempty"` | ||
| IP string `json:"ip,omitempty"` | ||
| MAC string `json:"mac,omitempty"` | ||
| } | ||
|
|
||
| // marshalUnderlyingResources serializes application-specific data into a | ||
| // runtime.RawExtension suitable for Backup.Status.UnderlyingResources. | ||
| func marshalUnderlyingResources(data interface{}) (*runtime.RawExtension, error) { | ||
| raw, err := json.Marshal(data) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return &runtime.RawExtension{Raw: raw}, nil | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Get the file and examine the specific lines mentioned
cat -n internal/backupcontroller/velerostrategy_controller.go | sed -n '78,110p'Repository: cozystack/cozystack
Length of output: 1614
🏁 Script executed:
# Check the API documentation for the underlyingResources field
find . -name "backup_types.go" | grep -E "api.*v1alpha1"Repository: cozystack/cozystack
Length of output: 101
🏁 Script executed:
# Search for getVMInstanceResources and its implementation
rg "getVMInstanceResources" -A 20 internal/Repository: cozystack/cozystack
Length of output: 10038
🏁 Script executed:
# Verify the marshal call context around line 320-324
cat -n internal/backupcontroller/velerostrategy_controller.go | sed -n '315,330p'Repository: cozystack/cozystack
Length of output: 660
🏁 Script executed:
# Check the API documentation for underlyingResources
cat -n api/backups/v1alpha1/backup_types.go | head -200Repository: cozystack/cozystack
Length of output: 5644
🏁 Script executed:
# Search for underlyingResources documentation
rg "underlyingResources" -B 5 -A 5 api/backups/v1alpha1/backup_types.goRepository: cozystack/cozystack
Length of output: 548
🏁 Script executed:
# Check the larger context to understand where vmInstanceResources is created
cat -n internal/backupcontroller/velerostrategy_controller.go | sed -n '260,325p'Repository: cozystack/cozystack
Length of output: 2560
🏁 Script executed:
# Check if there's any Kind being set for vmInstanceResources anywhere
rg "vmInstanceKind" internal/backupcontroller/velerostrategy_controller.goRepository: cozystack/cozystack
Length of output: 196
🏁 Script executed:
# Verify there are no other places where vmInstanceResources is marshaled
rg "vmInstanceResources\{" internal/backupcontroller/velerostrategy_controller.goRepository: cozystack/cozystack
Length of output: 118
🏁 Script executed:
# Check if there are constants for vmInstanceKind that could be used
rg "vmInstanceKind|vmDiskAppKind" internal/backupcontroller/velerostrategy_controller.go | head -20Repository: cozystack/cozystack
Length of output: 298
Add TypeMeta to the marshaled vmInstanceResources blob to match the documented contract.
The API documentation for UnderlyingResources (backup_types.go) explicitly states it should be "a self-typed JSON object carrying an inlined TypeMeta (kind/apiVersion) so the consuming controller can dispatch on the application kind." However, the current implementation marshals a flat vmInstanceResources struct without metav1.TypeMeta, violating this contract. Additionally, getVMInstanceResources does not validate Kind from the blob and instead delegates to caller-side responsibility, which prevents safe dispatch and creates a risk that future non-VM payloads with overlapping fields could be misdecoded.
Embed metav1.TypeMeta in vmInstanceResources, populate Kind and APIVersion during marshaling using the available vmInstanceKind constant, and validate the Kind on decode. Preserve backward compatibility by accepting payloads without Kind as a legacy fallback.
🔧 Possible fix
type vmInstanceResources struct {
+ metav1.TypeMeta `json:",inline"`
DataVolumes []backupsv1alpha1.DataVolumeResource `json:"dataVolumes,omitempty"`
IP string `json:"ip,omitempty"`
MAC string `json:"mac,omitempty"`
}
func getVMInstanceResources(ur *runtime.RawExtension) *vmInstanceResources {
if ur == nil || len(ur.Raw) == 0 {
return nil
}
+ var tm metav1.TypeMeta
+ if err := json.Unmarshal(ur.Raw, &tm); err != nil {
+ return nil
+ }
+ if tm.Kind != "" && tm.Kind != vmInstanceKind {
+ return nil
+ }
var res vmInstanceResources
if err := json.Unmarshal(ur.Raw, &res); err != nil {
return nil
}
if len(res.DataVolumes) == 0 && res.IP == "" && res.MAC == "" {
return nil
}
return &res
}
return marshalUnderlyingResources(vmInstanceResources{
+ TypeMeta: metav1.TypeMeta{
+ Kind: vmInstanceKind,
+ },
DataVolumes: dataVolumes,
IP: ip,
MAC: mac,
})Also applies to: 95–110, 320–324
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@internal/backupcontroller/velerostrategy_controller.go` around lines 78 - 93,
The marshaled vmInstanceResources must include Kubernetes TypeMeta and decoding
must validate Kind: update the vmInstanceResources struct to embed
metav1.TypeMeta, modify marshalUnderlyingResources (or a vm-specific wrapper
used there) to set TypeMeta.Kind = vmInstanceKind and TypeMeta.APIVersion to the
appropriate group/version before JSON marshalling, and update
getVMInstanceResources to check the decoded object's Kind equals vmInstanceKind
(return an error if it mismatches) while preserving backward compatibility by
accepting and decoding payloads that lack a Kind field as a legacy fallback; use
the vmInstanceResources type and vmInstanceKind constant to locate the affected
code.
What this PR does
Release note
now it looks like:
Summary by CodeRabbit