Change:
import ( "encoding/json" <ins> "hash/crc32" </ins> <del> "k8s.io/kubernetes/pkg/kubelet/checkpointmanager" "k8s.io/kubernetes/pkg/kubelet/checkpointmanager/checksum" </del> <ins> metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/kubernetes/pkg/kubelet/checkpointmanager/errors" </ins> ) <del> var _ checkpointmanager.Checkpoint = &DRAManagerCheckpoint{} </del> <ins> const ( CheckpointAPIGroup   = "checkpoint.dra.kubelet.k8s.io" CheckpointKind       = "DRACheckpoint" CheckpointAPIVersion = CheckpointAPIGroup + "/v1" ) </ins> <del> const checkpointVersion = "v1" </del> <ins> // Checkpoint represents a structure to store DRA checkpoint data type Checkpoint struct { // Data is a JSON serialized checkpoint data Data string // Checksum is a checksum of Data Checksum uint32 } </ins> <del> // DRAManagerCheckpoint struct is used to store pod dynamic resources assignments in a checkpoint type DRAManagerCheckpoint struct { Version  string             `json:"version"` Entries  ClaimInfoStateList `json:"entries,omitempty"` Checksum checksum.Checksum  `json:"checksum"` </del> <ins> type CheckpointData struct { metav1.TypeMeta ClaimInfoStateList ClaimInfoStateList </ins> } <del> // List of claim info to store in checkpoint type ClaimInfoStateList []ClaimInfoState </del> <ins> // NewCheckpoint creates a new checkpoint from a list of claim info states func NewCheckpoint(data ClaimInfoStateList) (*Checkpoint, error) { cpData := &CheckpointData{ TypeMeta: metav1.TypeMeta{ Kind:       CheckpointKind, APIVersion: CheckpointAPIVersion, }, ClaimInfoStateList: data, } cpDataBytes, err := json.Marshal(cpData) if err != nil { return nil, err } </ins> <del> // NewDRAManagerCheckpoint returns an instance of Checkpoint func NewDRAManagerCheckpoint() *DRAManagerCheckpoint { return &DRAManagerCheckpoint{ Version: checkpointVersion, Entries: ClaimInfoStateList{}, </del> <ins> cp := &Checkpoint{ Data:     string(cpDataBytes), Checksum: crc32.ChecksumIEEE(cpDataBytes), </ins> } <ins> return cp, nil </ins> } <del> // MarshalCheckpoint returns marshalled checkpoint func (dc *DRAManagerCheckpoint) MarshalCheckpoint() ([]byte, error) { // make sure checksum wasn't set before so it doesn't affect output checksum dc.Checksum = 0 dc.Checksum = checksum.New(dc) return json.Marshal(*dc) </del> <ins> // MarshalCheckpoint marshals checkpoint to JSON func (cp *Checkpoint) MarshalCheckpoint() ([]byte, error) { return json.Marshal(cp) </ins> } <del> // UnmarshalCheckpoint tries to unmarshal passed bytes to checkpoint func (dc *DRAManagerCheckpoint) UnmarshalCheckpoint(blob []byte) error { return json.Unmarshal(blob, dc) </del> <ins> // UnmarshalCheckpoint unmarshals checkpoint from JSON // and verifies its data checksum func (cp *Checkpoint) UnmarshalCheckpoint(blob []byte) error { if err := json.Unmarshal(blob, cp); err != nil { return err } // verify checksum if err := cp.VerifyChecksum(); err != nil { return err } return nil </ins> } <del> // VerifyChecksum verifies that current checksum of checkpoint is valid func (dc *DRAManagerCheckpoint) VerifyChecksum() error { ck := dc.Checksum dc.Checksum = 0 err := ck.Verify(dc) dc.Checksum = ck return err </del> <ins> // VerifyChecksum verifies that current checksum // of checkpointed Data is valid func (cp *Checkpoint) VerifyChecksum() error { expectedCS := crc32.ChecksumIEEE([]byte(cp.Data)) if expectedCS != cp.Checksum { return &errors.CorruptCheckpointError{ActualCS: uint64(cp.Checksum), ExpectedCS: uint64(expectedCS)} } return nil } // GetClaimInfoStateList returns list of claim info states from checkpoint func (cp *Checkpoint) GetClaimInfoStateList() (ClaimInfoStateList, error) { var data CheckpointData if err := json.Unmarshal([]byte(cp.Data), &data); err != nil { return nil, err } return data.ClaimInfoStateList, nil </ins> }

Discussion:
When adding the field to , old checkpoint files became invalid because the checksum that gets calculated by hashing the output in no longer matches. This caused kubelet to fail to start with: Here's what the checksum in a unit test gets calculcated for: Note the new . Not sure what the right behavior is. Perhaps kubelet should have ignored the invalid checkpoint? Revert the update in The state.DRAManagerCheckpoint contains a version field, which is set to v1.I think that's where things fall apart: that version must exactly match the type that gets serialized, which is not the case here. The type changes over time, but the version is fixed. Let's reconsider what should get checkpointed and how we can make that stable. Also, is checkpointing really worth it? What bad things happen if we remove it? master n/a No response No response No response /sig node /cc
/cc
We originally didn't have checkpointing and it was by in to fix
The reasons for checkpoint were: (what Kevin mentioned) of the pod resource API DAR extensions concerns raised on what happen if kubelet restart and we get GetPodResourcesRequest. The request should return the DRA claimed devices but if we didn't checkpoint the DRA cache is empty and we would return empty resources force delete see [2] My original PR didn't checkpoint the ResourceHandle is was in PR [2]. Maybe we should exclude ResourceHandle from the checksum hash calculation [1] [2] -
So the cache makes sense. What doesn't make sense to me is how the consistency of that on-disk file is checked and how it is versioned. Shouldn't the checksum be calculated for the bytes on disk (version-independent) instead of the in-memory representation (version-dependent)? Or is the goal to protect against "I can't read this data anymore" instead of "bit has flipped on disk"? The assumption is that the content can always be decoded from what was written in the past into the type which is used now. This works with JSON if the newer type is a true superset of the old one, but that's not necessarily guaranteed. The proper way of doing this is to define a scheme which includes conversion between different versions.

This cannot be done when embedding API types from because conversion between current v1alpha2 and a future v1beta1 can only be done in the apiserver, not client-side. I don't see a quick solution here. In the short-term, how do we deal with version upgrades? Require that users drain the node (thus clearing the cache) before upgrading or downgrading kubelet?
I am still trying to understand the problem. the problem that we checksum the version? the problem that we don't have conversion mechanism to move from version X -version Y on upgrade? the issue related only for upgrade or there are other cases which checksum changes? In general we the version to checkpoint file so that in the future when the DRA checkpoint struct changed we can have mechanism to convert old checkpoint version to new checkpoint version (in the initial PR we didn't implement conversion mechanism as I thought it will be on DRA checkpoint struct change) . If you take a look on the CPU Manager checkpoint [1] you will see that it detecting if the scheme matches to V1 first and if not it assume V2. We wanted that it will part of the checkpoint file so we won't need such detection and in the file we will know that correct version of the DRA checkpoint struct. Now if someone change the DRA checkpoint scheme and didn't implement migration flow (similar to CPU manager [2]) or change the version that indeed an issue, [1] - [2] -
The problem is that after an upgrade of kubelet from <1.30 to 1.30, it won't be able to read an existing, non-empty checkpoint file. Worse, it will refuse to start because of that. The root cause is the lack of proper versioning and lack of conversion code. Any version change potentially has this problem (including downgrades). I'm not aware of other failure scenarios.
Since it is not a GA feature, we cannot fix this in 1.30. But marking it as important long-term. /priority important-longterm
This has to be solved before graduating from alpha
I intend to move all version-dependent code into drivers (). This will simplify the checkpoint content a bit, but the overall questions around its design remain.

/kind feature /remove-kind bug /triage accepted I think a good forum to discuss this could be the KEP for DRA, though we can keep this tracker here
Isn't it because checkpoint structure was changed without incrementing version and adding conversion code? Anyway, I'd propose to remove version from the checkpoint structure and comment it to prevent changing it without changing its version. the implementation should be similar to the cpu maanager checkpointing. If it's been working for CPU (and device?) managers for some time, it should hopefully work for DRA as well. Any other ideas?
Yes. I haven't looked into this again. Can you implement your proposal on top of and submit a PR for review, or do you want to discuss this first?
if you're ok with my proposal I'm ok to implement it. Otherwise it needs to be discussed further.
I don't fully understand the implications without looking into this further, so I can say neither yes nor no to it at this point. do you have an opinion?