mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-17 11:28:58 +00:00
CPU & Memory managers forward compatibility
This commit is contained in:
@@ -100,7 +100,17 @@ func (cp *CPUManagerCheckpointV1) MarshalCheckpoint() ([]byte, error) {
|
||||
func (cp *CPUManagerCheckpointV2) MarshalCheckpoint() ([]byte, error) {
|
||||
// make sure checksum wasn't set before so it doesn't affect output checksum
|
||||
cp.Checksum = 0
|
||||
cp.Checksum = checksum.New(cp)
|
||||
|
||||
// In order to preserve rollback compatibility when the feature gate is disabled,
|
||||
// we must generate a checksum using the legacy struct name "CPUManagerCheckpoint"
|
||||
// instead of "CPUManagerCheckpointV2". Older Kubelets do not have the string
|
||||
// replacement logic and expect the original struct name.
|
||||
object := dump.ForHash(cp)
|
||||
object = strings.Replace(object, "CPUManagerCheckpointV2", "CPUManagerCheckpoint", 1)
|
||||
hash := fnv.New32a()
|
||||
_, _ = fmt.Fprintf(hash, "%v", object)
|
||||
cp.Checksum = checksum.Checksum(hash.Sum32())
|
||||
|
||||
return json.Marshal(*cp)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package state
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
|
||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager/checksum"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/containermap"
|
||||
testutil "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state/testing"
|
||||
"k8s.io/kubernetes/test/utils/ktesting"
|
||||
@@ -637,3 +639,53 @@ func AssertStateEqual(t *testing.T, sf State, sm State) {
|
||||
t.Errorf("State CPU assignments mismatch. Have %s, want %s", cpuassignmentSf, cpuassignmentSm)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCPUManagerCheckpointV2_MarshalCheckpoint_ForwardCompatibility(t *testing.T) {
|
||||
// 1. Create a V2 checkpoint using the struct defined in the current codebase (1.36+)
|
||||
currentCheckpoint := &CPUManagerCheckpointV2{
|
||||
PolicyName: "none",
|
||||
DefaultCPUSet: "1-3",
|
||||
Entries: make(map[string]map[string]string),
|
||||
}
|
||||
|
||||
// Marshal it using the logic that forces the "CPUManagerCheckpoint" name
|
||||
data, err := currentCheckpoint.MarshalCheckpoint()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal checkpoint: %v", err)
|
||||
}
|
||||
|
||||
// 2. Unmarshal the raw JSON to extract the checksum that was actually written to the file
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal(data, &result); err != nil {
|
||||
t.Fatalf("Failed to unmarshal JSON: %v", err)
|
||||
}
|
||||
|
||||
actualChecksumFloat, ok := result["checksum"].(float64)
|
||||
if !ok {
|
||||
t.Fatalf("Checksum field missing or invalid type")
|
||||
}
|
||||
writtenChecksum := checksum.Checksum(uint64(actualChecksumFloat))
|
||||
|
||||
// 3. Reconstruct how versions 1.35 and earlier would calculate the checksum
|
||||
// by defining a struct with the exact legacy name and fields.
|
||||
type CPUManagerCheckpoint struct {
|
||||
PolicyName string `json:"policyName"`
|
||||
DefaultCPUSet string `json:"defaultCpuSet"`
|
||||
Entries map[string]map[string]string `json:"entries,omitempty"`
|
||||
Checksum checksum.Checksum `json:"checksum"`
|
||||
}
|
||||
|
||||
legacyCheckpoint := &CPUManagerCheckpoint{
|
||||
PolicyName: currentCheckpoint.PolicyName,
|
||||
DefaultCPUSet: currentCheckpoint.DefaultCPUSet,
|
||||
Entries: currentCheckpoint.Entries,
|
||||
}
|
||||
|
||||
expectedLegacyChecksum := checksum.New(legacyCheckpoint)
|
||||
|
||||
// 4. Assert that the checksum written by our 1.36+ code matches
|
||||
// what a 1.35 Kubelet would expect to see.
|
||||
if writtenChecksum != expectedLegacyChecksum {
|
||||
t.Errorf("Written Checksum %d does not match legacy calculation %d. Forward compatibility broken.", writtenChecksum, expectedLegacyChecksum)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,17 @@ func newMemoryManagerCheckpointV2() *MemoryManagerCheckpointV2 {
|
||||
func (mp *MemoryManagerCheckpointV1) MarshalCheckpoint() ([]byte, error) {
|
||||
// make sure checksum wasn't set before so it doesn't affect output checksum
|
||||
mp.Checksum = 0
|
||||
mp.Checksum = checksum.New(mp)
|
||||
|
||||
// In order to preserve rollback compatibility when the feature gate is disabled,
|
||||
// we must generate a checksum using the legacy struct name "MemoryManagerCheckpoint"
|
||||
// instead of "MemoryManagerCheckpointV1". Older Kubelets do not have the string
|
||||
// replacement logic and expect the original struct name.
|
||||
object := dump.ForHash(mp)
|
||||
object = strings.Replace(object, "MemoryManagerCheckpointV1", "MemoryManagerCheckpoint", 1)
|
||||
hash := fnv.New32a()
|
||||
_, _ = fmt.Fprintf(hash, "%v", object)
|
||||
mp.Checksum = checksum.Checksum(hash.Sum32())
|
||||
|
||||
return json.Marshal(*mp)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package state
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -29,6 +30,7 @@ import (
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
|
||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager/checksum"
|
||||
testutil "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state/testing"
|
||||
"k8s.io/kubernetes/test/utils/ktesting"
|
||||
)
|
||||
@@ -592,3 +594,53 @@ func TestCheckpointStateClear(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemoryManagerCheckpointV1_MarshalCheckpoint_ForwardCompatibility(t *testing.T) {
|
||||
// 1. Create a V1 checkpoint using the struct defined in the current codebase (1.36+)
|
||||
currentCheckpoint := &MemoryManagerCheckpointV1{
|
||||
PolicyName: "none",
|
||||
MachineState: NUMANodeMap{},
|
||||
Entries: ContainerMemoryAssignments{},
|
||||
}
|
||||
|
||||
// Marshal it using the logic that forces the "MemoryManagerCheckpoint" name
|
||||
data, err := currentCheckpoint.MarshalCheckpoint()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal checkpoint: %v", err)
|
||||
}
|
||||
|
||||
// 2. Unmarshal the raw JSON to extract the checksum that was actually written to the file
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal(data, &result); err != nil {
|
||||
t.Fatalf("Failed to unmarshal JSON: %v", err)
|
||||
}
|
||||
|
||||
actualChecksumFloat, ok := result["checksum"].(float64)
|
||||
if !ok {
|
||||
t.Fatalf("Checksum field missing or invalid type")
|
||||
}
|
||||
writtenChecksum := checksum.Checksum(uint64(actualChecksumFloat))
|
||||
|
||||
// 3. Reconstruct how versions 1.35 and earlier would calculate the checksum
|
||||
// by defining a struct with the exact legacy name and fields.
|
||||
type MemoryManagerCheckpoint struct {
|
||||
PolicyName string `json:"policyName"`
|
||||
MachineState NUMANodeMap `json:"machineState"`
|
||||
Entries ContainerMemoryAssignments `json:"entries,omitempty"`
|
||||
Checksum checksum.Checksum `json:"checksum"`
|
||||
}
|
||||
|
||||
legacyCheckpoint := &MemoryManagerCheckpoint{
|
||||
PolicyName: currentCheckpoint.PolicyName,
|
||||
MachineState: currentCheckpoint.MachineState,
|
||||
Entries: currentCheckpoint.Entries,
|
||||
}
|
||||
|
||||
expectedLegacyChecksum := checksum.New(legacyCheckpoint)
|
||||
|
||||
// 4. Assert that the checksum written by our 1.36+ code matches
|
||||
// what a 1.35 Kubelet would expect to see.
|
||||
if writtenChecksum != expectedLegacyChecksum {
|
||||
t.Errorf("Written Checksum %d does not match legacy calculation %d. Forward compatibility broken.", writtenChecksum, expectedLegacyChecksum)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user