@@ -23,7 +23,12 @@ import (
2323 "k8s.io/kubernetes/pkg/kubelet/checkpointmanager/checksum"
2424)
2525
26+ const CheckpointVersion = "v2"
27+
2628type Checkpoint struct {
29+ // Version records the latest checkpoint version written, allowing readers
30+ // to quickly determine the format without inspecting nested fields.
31+ Version string `json:"version,omitempty"`
2732 // Note: The Checksum below is only associated with the V1 checkpoint
2833 // (because it doesn't have an embedded one). All future versions have
2934 // their checksum directly embedded in them to better support
@@ -32,6 +37,95 @@ type Checkpoint struct {
3237 Checksum checksum.Checksum `json:"checksum"`
3338 V1 * CheckpointV1 `json:"v1,omitempty"`
3439 V2 * CheckpointV2 `json:"v2,omitempty"`
40+ // other holds unknown fields from a newer checkpoint format, preserved
41+ // so that a downgraded driver can round-trip data it does not understand.
42+ other map [string ]json.RawMessage
43+ }
44+
45+ // MarshalJSON implements json.Marshaler, merging known fields with any
46+ // unknown fields captured from a newer checkpoint format.
47+ func (cp * Checkpoint ) MarshalJSON () ([]byte , error ) {
48+ type Alias struct {
49+ Version string `json:"version,omitempty"`
50+ Checksum checksum.Checksum `json:"checksum"`
51+ V1 * CheckpointV1 `json:"v1,omitempty"`
52+ V2 * CheckpointV2 `json:"v2,omitempty"`
53+ }
54+ known , err := json .Marshal (& Alias {
55+ Version : cp .Version ,
56+ Checksum : cp .Checksum ,
57+ V1 : cp .V1 ,
58+ V2 : cp .V2 ,
59+ })
60+ if err != nil {
61+ return nil , err
62+ }
63+ if len (cp .other ) == 0 {
64+ return known , nil
65+ }
66+ var merged map [string ]json.RawMessage
67+ if err := json .Unmarshal (known , & merged ); err != nil {
68+ return nil , err
69+ }
70+ for k , v := range cp .other {
71+ merged [k ] = v
72+ }
73+ return json .Marshal (merged )
74+ }
75+
76+ // UnmarshalJSON implements json.Unmarshaler, populating known fields and
77+ // preserving any unrecognised fields (future versions) in cp.other.
78+ func (cp * Checkpoint ) UnmarshalJSON (data []byte ) error {
79+ type Alias struct {
80+ Version string `json:"version,omitempty"`
81+ Checksum checksum.Checksum `json:"checksum"`
82+ V1 * CheckpointV1 `json:"v1,omitempty"`
83+ V2 * CheckpointV2 `json:"v2,omitempty"`
84+ }
85+ var alias Alias
86+ if err := json .Unmarshal (data , & alias ); err != nil {
87+ return err
88+ }
89+ cp .Version = alias .Version
90+ cp .Checksum = alias .Checksum
91+ cp .V1 = alias .V1
92+ cp .V2 = alias .V2
93+
94+ var all map [string ]json.RawMessage
95+ if err := json .Unmarshal (data , & all ); err != nil {
96+ return err
97+ }
98+ delete (all , "version" )
99+ delete (all , "checksum" )
100+ delete (all , "v1" )
101+ delete (all , "v2" )
102+ if len (all ) > 0 {
103+ cp .other = all
104+ } else {
105+ cp .other = nil
106+ }
107+ return nil
108+ }
109+
110+ func (cp * Checkpoint ) DeepCopy () * Checkpoint {
111+ if cp == nil {
112+ return nil
113+ }
114+ out := & Checkpoint {
115+ Version : cp .Version ,
116+ Checksum : cp .Checksum ,
117+ V1 : cp .V1 .DeepCopy (),
118+ V2 : cp .V2 .DeepCopy (),
119+ }
120+ if len (cp .other ) > 0 {
121+ out .other = make (map [string ]json.RawMessage , len (cp .other ))
122+ for k , v := range cp .other {
123+ raw := make (json.RawMessage , len (v ))
124+ copy (raw , v )
125+ out .other [k ] = raw
126+ }
127+ }
128+ return out
35129}
36130
37131func (cp * Checkpoint ) ToLatestVersion () * Checkpoint {
@@ -52,25 +146,27 @@ func (cp *Checkpoint) ToLatestVersion() *Checkpoint {
52146
53147func (cp * Checkpoint ) MarshalCheckpoint () ([]byte , error ) {
54148 cp = cp .ToLatestVersion ()
149+ cp .Version = CheckpointVersion
55150 cp .V1 = cp .V2 .ToV1 ()
56151 if err := cp .SetChecksumV1 (); err != nil {
57152 return nil , fmt .Errorf ("error setting v1 checksum: %v" , err )
58153 }
59154 if err := cp .SetChecksumV2 (); err != nil {
60155 return nil , fmt .Errorf ("error setting v2 checksum: %v" , err )
61156 }
62- return json .Marshal (* cp )
157+ return json .Marshal (cp )
63158}
64159
160+ // SetChecksumV1 computes and sets the V1 checksum, which covers only the
161+ // V1 view of the checkpoint (Version, V2, and other are excluded so that
162+ // older drivers computing the same checksum get identical JSON).
65163func (cp * Checkpoint ) SetChecksumV1 () error {
66- v2 := cp .V2
67- cp .V2 = nil
68- defer func () {
69- cp .V2 = v2
70- }()
71-
72- cp .Checksum = 0
73- out , err := json .Marshal (* cp )
164+ type v1View struct {
165+ Checksum checksum.Checksum `json:"checksum"`
166+ V1 * CheckpointV1 `json:"v1,omitempty"`
167+ }
168+ view := v1View {V1 : cp .V1 }
169+ out , err := json .Marshal (view )
74170 if err != nil {
75171 return err
76172 }
@@ -126,22 +222,19 @@ func (cp *Checkpoint) VerifyChecksum() error {
126222 return nil
127223}
128224
225+ // VerifyChecksumV1 verifies the V1 checksum using the same V1-only view that
226+ // SetChecksumV1 used, ensuring older drivers can also verify successfully.
129227func (cp * Checkpoint ) VerifyChecksumV1 () error {
130- ck := cp .Checksum
131- v2 := cp .V2
132- cp .V2 = nil
133- defer func () {
134- cp .Checksum = ck
135- cp .V2 = v2
136- }()
137-
138- cp .Checksum = 0
139- out , err := json .Marshal (* cp )
228+ type v1View struct {
229+ Checksum checksum.Checksum `json:"checksum"`
230+ V1 * CheckpointV1 `json:"v1,omitempty"`
231+ }
232+ view := v1View {V1 : cp .V1 }
233+ out , err := json .Marshal (view )
140234 if err != nil {
141235 return err
142236 }
143-
144- return ck .Verify (out )
237+ return cp .Checksum .Verify (out )
145238}
146239
147240func (cp * Checkpoint ) VerifyChecksumV2 () error {
0 commit comments