mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
cpumanager: drop old custom file backend
The cpumanager file-based state backend was obsoleted since few releases, aving the cpumanager moved to the checkpointmanager common infrastructure. The old test checking compatibility to/from the old format is also no longer needed, because the checkpoint format is stable (see https://github.com/kubernetes/kubernetes/tree/master/pkg/kubelet/checkpointmanager). Signed-off-by: Francesco Romani <fromani@redhat.com>
This commit is contained in:
parent
15bb54c2d2
commit
be0fe3df9b
@ -6,7 +6,6 @@ go_library(
|
|||||||
"checkpoint.go",
|
"checkpoint.go",
|
||||||
"state.go",
|
"state.go",
|
||||||
"state_checkpoint.go",
|
"state_checkpoint.go",
|
||||||
"state_file.go",
|
|
||||||
"state_mem.go",
|
"state_mem.go",
|
||||||
],
|
],
|
||||||
importpath = "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state",
|
importpath = "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state",
|
||||||
@ -24,18 +23,13 @@ go_library(
|
|||||||
|
|
||||||
go_test(
|
go_test(
|
||||||
name = "go_default_test",
|
name = "go_default_test",
|
||||||
srcs = [
|
srcs = ["state_checkpoint_test.go"],
|
||||||
"state_checkpoint_test.go",
|
|
||||||
"state_compatibility_test.go",
|
|
||||||
"state_file_test.go",
|
|
||||||
],
|
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/kubelet/checkpointmanager:go_default_library",
|
"//pkg/kubelet/checkpointmanager:go_default_library",
|
||||||
"//pkg/kubelet/cm/containermap:go_default_library",
|
"//pkg/kubelet/cm/containermap:go_default_library",
|
||||||
"//pkg/kubelet/cm/cpumanager/state/testing:go_default_library",
|
"//pkg/kubelet/cm/cpumanager/state/testing:go_default_library",
|
||||||
"//pkg/kubelet/cm/cpuset:go_default_library",
|
"//pkg/kubelet/cm/cpuset:go_default_library",
|
||||||
"//vendor/github.com/stretchr/testify/require:go_default_library",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
|
||||||
)
|
|
||||||
|
|
||||||
const compatibilityTestingCheckpoint = "cpumanager_state_compatibility_test"
|
|
||||||
|
|
||||||
var state = &stateMemory{
|
|
||||||
assignments: ContainerCPUAssignments{
|
|
||||||
"pod": map[string]cpuset.CPUSet{
|
|
||||||
"container1": cpuset.NewCPUSet(4, 5, 6),
|
|
||||||
"container2": cpuset.NewCPUSet(1, 2, 3),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultCPUSet: cpuset.NewCPUSet(1, 2, 3),
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFileToCheckpointCompatibility(t *testing.T) {
|
|
||||||
statePath := path.Join(testingDir, compatibilityTestingCheckpoint)
|
|
||||||
|
|
||||||
// ensure there is no previous state saved at testing path
|
|
||||||
os.Remove(statePath)
|
|
||||||
// ensure testing state is removed after testing
|
|
||||||
defer os.Remove(statePath)
|
|
||||||
|
|
||||||
fileState, err := NewFileState(statePath, "none", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("could not create new file state: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileState.SetDefaultCPUSet(state.defaultCPUSet)
|
|
||||||
fileState.SetCPUAssignments(state.assignments)
|
|
||||||
|
|
||||||
restoredState, err := NewCheckpointState(testingDir, compatibilityTestingCheckpoint, "none", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("could not restore file state: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
AssertStateEqual(t, restoredState, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckpointToFileCompatibility(t *testing.T) {
|
|
||||||
cpm, err := checkpointmanager.NewCheckpointManager(testingDir)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("could not create testing checkpoint manager: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure there is no previous checkpoint
|
|
||||||
cpm.RemoveCheckpoint(compatibilityTestingCheckpoint)
|
|
||||||
// ensure testing checkpoint is removed after testing
|
|
||||||
defer cpm.RemoveCheckpoint(compatibilityTestingCheckpoint)
|
|
||||||
|
|
||||||
checkpointState, err := NewCheckpointState(testingDir, compatibilityTestingCheckpoint, "none", nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
checkpointState.SetDefaultCPUSet(state.defaultCPUSet)
|
|
||||||
checkpointState.SetCPUAssignments(state.assignments)
|
|
||||||
|
|
||||||
restoredState, err := NewFileState(path.Join(testingDir, compatibilityTestingCheckpoint), "none", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("could not create new file state: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
AssertStateEqual(t, restoredState, state)
|
|
||||||
}
|
|
@ -1,278 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"k8s.io/klog"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm/containermap"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stateFileDataV1 struct {
|
|
||||||
PolicyName string `json:"policyName"`
|
|
||||||
DefaultCPUSet string `json:"defaultCpuSet"`
|
|
||||||
Entries map[string]string `json:"entries,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type stateFileDataV2 struct {
|
|
||||||
PolicyName string `json:"policyName"`
|
|
||||||
DefaultCPUSet string `json:"defaultCpuSet"`
|
|
||||||
Entries map[string]map[string]string `json:"entries,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ State = &stateFile{}
|
|
||||||
|
|
||||||
type stateFile struct {
|
|
||||||
sync.RWMutex
|
|
||||||
stateFilePath string
|
|
||||||
policyName string
|
|
||||||
cache State
|
|
||||||
initialContainers containermap.ContainerMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFileState creates new State for keeping track of cpu/pod assignment with file backend
|
|
||||||
func NewFileState(filePath string, policyName string, initialContainers containermap.ContainerMap) (State, error) {
|
|
||||||
stateFile := &stateFile{
|
|
||||||
stateFilePath: filePath,
|
|
||||||
cache: NewMemoryState(),
|
|
||||||
policyName: policyName,
|
|
||||||
initialContainers: initialContainers,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := stateFile.tryRestoreState(); err != nil {
|
|
||||||
// could not restore state, init new state file
|
|
||||||
klog.Errorf("[cpumanager] state file: unable to restore state from disk (%v)"+
|
|
||||||
" We cannot guarantee sane CPU affinity for existing containers."+
|
|
||||||
" Please drain this node and delete the CPU manager state file \"%s\" before restarting Kubelet.",
|
|
||||||
err, stateFile.stateFilePath)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return stateFile, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// migrateV1StateToV2State() converts state from the v1 format to the v2 format
|
|
||||||
func (sf *stateFile) migrateV1StateToV2State(src *stateFileDataV1, dst *stateFileDataV2) error {
|
|
||||||
if src.PolicyName != "" {
|
|
||||||
dst.PolicyName = src.PolicyName
|
|
||||||
}
|
|
||||||
if src.DefaultCPUSet != "" {
|
|
||||||
dst.DefaultCPUSet = src.DefaultCPUSet
|
|
||||||
}
|
|
||||||
for containerID, cset := range src.Entries {
|
|
||||||
podUID, containerName, err := sf.initialContainers.GetContainerRef(containerID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("containerID '%v' not found in initial containers list", containerID)
|
|
||||||
}
|
|
||||||
if dst.Entries == nil {
|
|
||||||
dst.Entries = make(map[string]map[string]string)
|
|
||||||
}
|
|
||||||
if _, exists := dst.Entries[podUID]; !exists {
|
|
||||||
dst.Entries[podUID] = make(map[string]string)
|
|
||||||
}
|
|
||||||
dst.Entries[podUID][containerName] = cset
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// tryRestoreState tries to read state file, upon any error,
|
|
||||||
// err message is logged and state is left clean. un-initialized
|
|
||||||
func (sf *stateFile) tryRestoreState() error {
|
|
||||||
sf.Lock()
|
|
||||||
defer sf.Unlock()
|
|
||||||
var err error
|
|
||||||
var content []byte
|
|
||||||
|
|
||||||
content, err = ioutil.ReadFile(sf.stateFilePath)
|
|
||||||
|
|
||||||
// If the state file does not exist or has zero length, write a new file.
|
|
||||||
if os.IsNotExist(err) || len(content) == 0 {
|
|
||||||
err := sf.storeState()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
klog.Infof("[cpumanager] state file: created new state file \"%s\"", sf.stateFilePath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fail on any other file read error.
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// File exists; try to read it.
|
|
||||||
var readStateV1 stateFileDataV1
|
|
||||||
var readStateV2 stateFileDataV2
|
|
||||||
|
|
||||||
if err = json.Unmarshal(content, &readStateV1); err != nil {
|
|
||||||
readStateV1 = stateFileDataV1{} // reset it back to 0
|
|
||||||
if err = json.Unmarshal(content, &readStateV2); err != nil {
|
|
||||||
klog.Errorf("[cpumanager] state file: could not unmarshal, corrupted state file - \"%s\"", sf.stateFilePath)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = sf.migrateV1StateToV2State(&readStateV1, &readStateV2); err != nil {
|
|
||||||
klog.Errorf("[cpumanager] state file: could not migrate v1 state to v2 state - \"%s\"", sf.stateFilePath)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if sf.policyName != readStateV2.PolicyName {
|
|
||||||
return fmt.Errorf("policy configured \"%s\" != policy from state file \"%s\"", sf.policyName, readStateV2.PolicyName)
|
|
||||||
}
|
|
||||||
|
|
||||||
var tmpDefaultCPUSet cpuset.CPUSet
|
|
||||||
if tmpDefaultCPUSet, err = cpuset.Parse(readStateV2.DefaultCPUSet); err != nil {
|
|
||||||
klog.Errorf("[cpumanager] state file: could not parse state file - [defaultCpuSet:\"%s\"]", readStateV2.DefaultCPUSet)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var tmpContainerCPUSet cpuset.CPUSet
|
|
||||||
tmpAssignments := ContainerCPUAssignments{}
|
|
||||||
for pod := range readStateV2.Entries {
|
|
||||||
tmpAssignments[pod] = make(map[string]cpuset.CPUSet)
|
|
||||||
for container, cpuString := range readStateV2.Entries[pod] {
|
|
||||||
if tmpContainerCPUSet, err = cpuset.Parse(cpuString); err != nil {
|
|
||||||
klog.Errorf("[cpumanager] state file: could not parse state file - pod: %s, container: %s, cpuset: \"%s\"", pod, container, cpuString)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tmpAssignments[pod][container] = tmpContainerCPUSet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sf.cache.SetDefaultCPUSet(tmpDefaultCPUSet)
|
|
||||||
sf.cache.SetCPUAssignments(tmpAssignments)
|
|
||||||
|
|
||||||
klog.V(2).Infof("[cpumanager] state file: restored state from state file \"%s\"", sf.stateFilePath)
|
|
||||||
klog.V(2).Infof("[cpumanager] state file: defaultCPUSet: %s", tmpDefaultCPUSet.String())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// saves state to a file, caller is responsible for locking
|
|
||||||
func (sf *stateFile) storeState() error {
|
|
||||||
var content []byte
|
|
||||||
var err error
|
|
||||||
|
|
||||||
data := stateFileDataV2{
|
|
||||||
PolicyName: sf.policyName,
|
|
||||||
DefaultCPUSet: sf.cache.GetDefaultCPUSet().String(),
|
|
||||||
Entries: map[string]map[string]string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
assignments := sf.cache.GetCPUAssignments()
|
|
||||||
for pod := range assignments {
|
|
||||||
data.Entries[pod] = map[string]string{}
|
|
||||||
for container, cset := range assignments[pod] {
|
|
||||||
data.Entries[pod][container] = cset.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if content, err = json.Marshal(data); err != nil {
|
|
||||||
return fmt.Errorf("[cpumanager] state file: could not serialize state to json")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = ioutil.WriteFile(sf.stateFilePath, content, 0644); err != nil {
|
|
||||||
return fmt.Errorf("[cpumanager] state file not written")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *stateFile) GetCPUSet(podUID string, containerName string) (cpuset.CPUSet, bool) {
|
|
||||||
sf.RLock()
|
|
||||||
defer sf.RUnlock()
|
|
||||||
|
|
||||||
res, ok := sf.cache.GetCPUSet(podUID, containerName)
|
|
||||||
return res, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *stateFile) GetDefaultCPUSet() cpuset.CPUSet {
|
|
||||||
sf.RLock()
|
|
||||||
defer sf.RUnlock()
|
|
||||||
|
|
||||||
return sf.cache.GetDefaultCPUSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *stateFile) GetCPUSetOrDefault(podUID string, containerName string) cpuset.CPUSet {
|
|
||||||
sf.RLock()
|
|
||||||
defer sf.RUnlock()
|
|
||||||
|
|
||||||
return sf.cache.GetCPUSetOrDefault(podUID, containerName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *stateFile) GetCPUAssignments() ContainerCPUAssignments {
|
|
||||||
sf.RLock()
|
|
||||||
defer sf.RUnlock()
|
|
||||||
return sf.cache.GetCPUAssignments()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *stateFile) SetCPUSet(podUID string, containerName string, cset cpuset.CPUSet) {
|
|
||||||
sf.Lock()
|
|
||||||
defer sf.Unlock()
|
|
||||||
sf.cache.SetCPUSet(podUID, containerName, cset)
|
|
||||||
err := sf.storeState()
|
|
||||||
if err != nil {
|
|
||||||
klog.Warningf("store state to checkpoint error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *stateFile) SetDefaultCPUSet(cset cpuset.CPUSet) {
|
|
||||||
sf.Lock()
|
|
||||||
defer sf.Unlock()
|
|
||||||
sf.cache.SetDefaultCPUSet(cset)
|
|
||||||
err := sf.storeState()
|
|
||||||
if err != nil {
|
|
||||||
klog.Warningf("store state to checkpoint error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *stateFile) SetCPUAssignments(a ContainerCPUAssignments) {
|
|
||||||
sf.Lock()
|
|
||||||
defer sf.Unlock()
|
|
||||||
sf.cache.SetCPUAssignments(a)
|
|
||||||
err := sf.storeState()
|
|
||||||
if err != nil {
|
|
||||||
klog.Warningf("store state to checkpoint error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *stateFile) Delete(podUID string, containerName string) {
|
|
||||||
sf.Lock()
|
|
||||||
defer sf.Unlock()
|
|
||||||
sf.cache.Delete(podUID, containerName)
|
|
||||||
err := sf.storeState()
|
|
||||||
if err != nil {
|
|
||||||
klog.Warningf("store state to checkpoint error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *stateFile) ClearState() {
|
|
||||||
sf.Lock()
|
|
||||||
defer sf.Unlock()
|
|
||||||
sf.cache.ClearState()
|
|
||||||
err := sf.storeState()
|
|
||||||
if err != nil {
|
|
||||||
klog.Warningf("store state to checkpoint error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,534 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm/containermap"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
|
||||||
)
|
|
||||||
|
|
||||||
func writeToStateFile(statefile string, content string) {
|
|
||||||
ioutil.WriteFile(statefile, []byte(content), 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertStateEqual marks provided test as failed if provided states differ
|
|
||||||
func AssertStateEqual(t *testing.T, sf State, sm State) {
|
|
||||||
cpusetSf := sf.GetDefaultCPUSet()
|
|
||||||
cpusetSm := sm.GetDefaultCPUSet()
|
|
||||||
if !cpusetSf.Equals(cpusetSm) {
|
|
||||||
t.Errorf("State CPUSet mismatch. Have %v, want %v", cpusetSf, cpusetSm)
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuassignmentSf := sf.GetCPUAssignments()
|
|
||||||
cpuassignmentSm := sm.GetCPUAssignments()
|
|
||||||
if !reflect.DeepEqual(cpuassignmentSf, cpuassignmentSm) {
|
|
||||||
t.Errorf("State CPU assignments mismatch. Have %s, want %s", cpuassignmentSf, cpuassignmentSm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func stderrCapture(t *testing.T, f func() State) (bytes.Buffer, State) {
|
|
||||||
stderr := os.Stderr
|
|
||||||
|
|
||||||
readBuffer, writeBuffer, err := os.Pipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("cannot create pipe: %v", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Stderr = writeBuffer
|
|
||||||
var outputBuffer bytes.Buffer
|
|
||||||
|
|
||||||
state := f()
|
|
||||||
writeBuffer.Close()
|
|
||||||
io.Copy(&outputBuffer, readBuffer)
|
|
||||||
os.Stderr = stderr
|
|
||||||
|
|
||||||
return outputBuffer, state
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFileStateTryRestore(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
description string
|
|
||||||
stateFileContent string
|
|
||||||
policyName string
|
|
||||||
initialContainers containermap.ContainerMap
|
|
||||||
expErr string
|
|
||||||
expectedState *stateMemory
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"Invalid JSON - one byte file",
|
|
||||||
"\n",
|
|
||||||
"none",
|
|
||||||
containermap.ContainerMap{},
|
|
||||||
"[cpumanager] state file: unable to restore state from disk (unexpected end of JSON input)",
|
|
||||||
&stateMemory{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Invalid JSON - invalid content",
|
|
||||||
"{",
|
|
||||||
"none",
|
|
||||||
containermap.ContainerMap{},
|
|
||||||
"[cpumanager] state file: unable to restore state from disk (unexpected end of JSON input)",
|
|
||||||
&stateMemory{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Try restore defaultCPUSet only",
|
|
||||||
`{"policyName": "none", "defaultCpuSet": "4-6"}`,
|
|
||||||
"none",
|
|
||||||
containermap.ContainerMap{},
|
|
||||||
"",
|
|
||||||
&stateMemory{
|
|
||||||
assignments: ContainerCPUAssignments{},
|
|
||||||
defaultCPUSet: cpuset.NewCPUSet(4, 5, 6),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Try restore defaultCPUSet only - invalid name",
|
|
||||||
`{"policyName": "none", "defaultCpuSet" "4-6"}`,
|
|
||||||
"none",
|
|
||||||
containermap.ContainerMap{},
|
|
||||||
`[cpumanager] state file: unable to restore state from disk (invalid character '"' after object key)`,
|
|
||||||
&stateMemory{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Try restore assignments only",
|
|
||||||
`{
|
|
||||||
"policyName": "none",
|
|
||||||
"entries": {
|
|
||||||
"pod": {
|
|
||||||
"container1": "4-6",
|
|
||||||
"container2": "1-3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
"none",
|
|
||||||
containermap.ContainerMap{},
|
|
||||||
"",
|
|
||||||
&stateMemory{
|
|
||||||
assignments: ContainerCPUAssignments{
|
|
||||||
"pod": map[string]cpuset.CPUSet{
|
|
||||||
"container1": cpuset.NewCPUSet(4, 5, 6),
|
|
||||||
"container2": cpuset.NewCPUSet(1, 2, 3),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultCPUSet: cpuset.NewCPUSet(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Try restore invalid policy name",
|
|
||||||
`{
|
|
||||||
"policyName": "A",
|
|
||||||
"defaultCpuSet": "0-7",
|
|
||||||
"entries": {}
|
|
||||||
}`,
|
|
||||||
"B",
|
|
||||||
containermap.ContainerMap{},
|
|
||||||
`[cpumanager] state file: unable to restore state from disk (policy configured "B" != policy from state file "A")`,
|
|
||||||
&stateMemory{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Try restore invalid assignments",
|
|
||||||
`{"entries": }`,
|
|
||||||
"none",
|
|
||||||
containermap.ContainerMap{},
|
|
||||||
"[cpumanager] state file: unable to restore state from disk (invalid character '}' looking for beginning of value)",
|
|
||||||
&stateMemory{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Try restore valid file",
|
|
||||||
`{
|
|
||||||
"policyName": "none",
|
|
||||||
"defaultCpuSet": "23-24",
|
|
||||||
"entries": {
|
|
||||||
"pod": {
|
|
||||||
"container1": "4-6",
|
|
||||||
"container2": "1-3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
"none",
|
|
||||||
containermap.ContainerMap{},
|
|
||||||
"",
|
|
||||||
&stateMemory{
|
|
||||||
assignments: ContainerCPUAssignments{
|
|
||||||
"pod": map[string]cpuset.CPUSet{
|
|
||||||
"container1": cpuset.NewCPUSet(4, 5, 6),
|
|
||||||
"container2": cpuset.NewCPUSet(1, 2, 3),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultCPUSet: cpuset.NewCPUSet(23, 24),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Try restore un-parsable defaultCPUSet ",
|
|
||||||
`{
|
|
||||||
"policyName": "none",
|
|
||||||
"defaultCpuSet": "2-sd"
|
|
||||||
}`,
|
|
||||||
"none",
|
|
||||||
containermap.ContainerMap{},
|
|
||||||
`[cpumanager] state file: unable to restore state from disk (strconv.Atoi: parsing "sd": invalid syntax)`,
|
|
||||||
&stateMemory{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Try restore un-parsable assignments",
|
|
||||||
`{
|
|
||||||
"policyName": "none",
|
|
||||||
"defaultCpuSet": "23-24",
|
|
||||||
"entries": {
|
|
||||||
"pod": {
|
|
||||||
"container1": "p-6",
|
|
||||||
"container2": "1-3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
"none",
|
|
||||||
containermap.ContainerMap{},
|
|
||||||
`[cpumanager] state file: unable to restore state from disk (strconv.Atoi: parsing "p": invalid syntax)`,
|
|
||||||
&stateMemory{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tryRestoreState creates empty state file",
|
|
||||||
"",
|
|
||||||
"none",
|
|
||||||
containermap.ContainerMap{},
|
|
||||||
"",
|
|
||||||
&stateMemory{
|
|
||||||
assignments: ContainerCPUAssignments{},
|
|
||||||
defaultCPUSet: cpuset.NewCPUSet(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Try restore with migration",
|
|
||||||
`{
|
|
||||||
"policyName": "none",
|
|
||||||
"defaultCpuSet": "23-24",
|
|
||||||
"entries": {
|
|
||||||
"containerID1": "4-6",
|
|
||||||
"containerID2": "1-3"
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
"none",
|
|
||||||
func() containermap.ContainerMap {
|
|
||||||
cm := containermap.NewContainerMap()
|
|
||||||
cm.Add("pod", "container1", "containerID1")
|
|
||||||
cm.Add("pod", "container2", "containerID2")
|
|
||||||
return cm
|
|
||||||
}(),
|
|
||||||
"",
|
|
||||||
&stateMemory{
|
|
||||||
assignments: ContainerCPUAssignments{
|
|
||||||
"pod": map[string]cpuset.CPUSet{
|
|
||||||
"container1": cpuset.NewCPUSet(4, 5, 6),
|
|
||||||
"container2": cpuset.NewCPUSet(1, 2, 3),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultCPUSet: cpuset.NewCPUSet(23, 24),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx, tc := range testCases {
|
|
||||||
t.Run(tc.description, func(t *testing.T) {
|
|
||||||
sfilePath, err := ioutil.TempFile("/tmp", fmt.Sprintf("cpumanager_state_file_test_%d", idx))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("cannot create temporary file: %q", err.Error())
|
|
||||||
}
|
|
||||||
// Don't create state file, let tryRestoreState figure out that is should create
|
|
||||||
if tc.stateFileContent != "" {
|
|
||||||
writeToStateFile(sfilePath.Name(), tc.stateFileContent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always remove file - regardless of who created
|
|
||||||
defer os.Remove(sfilePath.Name())
|
|
||||||
|
|
||||||
logData, fileState := stderrCapture(t, func() State {
|
|
||||||
newFileState, _ := NewFileState(sfilePath.Name(), tc.policyName, tc.initialContainers)
|
|
||||||
return newFileState
|
|
||||||
})
|
|
||||||
|
|
||||||
if tc.expErr != "" {
|
|
||||||
if logData.String() != "" {
|
|
||||||
if !strings.Contains(logData.String(), tc.expErr) {
|
|
||||||
t.Errorf("tryRestoreState() error = %v, wantErr %v", logData.String(), tc.expErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("tryRestoreState() error = nil, wantErr %v", tc.expErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if fileState == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
AssertStateEqual(t, fileState, tc.expectedState)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFileStateTryRestoreError(t *testing.T) {
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
description string
|
|
||||||
expErr error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
" create file error",
|
|
||||||
fmt.Errorf("[cpumanager] state file not written"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
|
||||||
t.Run(testCase.description, func(t *testing.T) {
|
|
||||||
sfilePath := path.Join("/invalid_path/to_some_dir", "cpumanager_state_file_test")
|
|
||||||
_, err := NewFileState(sfilePath, "static", nil)
|
|
||||||
if !reflect.DeepEqual(err, testCase.expErr) {
|
|
||||||
t.Errorf("unexpected error, expected: %s, got: %s", testCase.expErr, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateStateFile(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
description string
|
|
||||||
expErr string
|
|
||||||
expectedState *stateMemory
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"Save empty state",
|
|
||||||
"",
|
|
||||||
&stateMemory{
|
|
||||||
assignments: ContainerCPUAssignments{},
|
|
||||||
defaultCPUSet: cpuset.NewCPUSet(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Save defaultCPUSet only",
|
|
||||||
"",
|
|
||||||
&stateMemory{
|
|
||||||
assignments: ContainerCPUAssignments{},
|
|
||||||
defaultCPUSet: cpuset.NewCPUSet(1, 6),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Save assignments only",
|
|
||||||
"",
|
|
||||||
&stateMemory{
|
|
||||||
assignments: ContainerCPUAssignments{
|
|
||||||
"pod": map[string]cpuset.CPUSet{
|
|
||||||
"container1": cpuset.NewCPUSet(4, 5, 6),
|
|
||||||
"container2": cpuset.NewCPUSet(1, 2, 3),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultCPUSet: cpuset.NewCPUSet(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx, tc := range testCases {
|
|
||||||
t.Run(tc.description, func(t *testing.T) {
|
|
||||||
|
|
||||||
sfilePath, err := ioutil.TempFile("/tmp", fmt.Sprintf("cpumanager_state_file_test_%d", idx))
|
|
||||||
defer os.Remove(sfilePath.Name())
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("cannot create temporary file: %q", err.Error())
|
|
||||||
}
|
|
||||||
fileState := stateFile{
|
|
||||||
stateFilePath: sfilePath.Name(),
|
|
||||||
policyName: "static",
|
|
||||||
cache: NewMemoryState(),
|
|
||||||
}
|
|
||||||
|
|
||||||
fileState.SetDefaultCPUSet(tc.expectedState.defaultCPUSet)
|
|
||||||
fileState.SetCPUAssignments(tc.expectedState.assignments)
|
|
||||||
|
|
||||||
logData, _ := stderrCapture(t, func() State {
|
|
||||||
fileState.storeState()
|
|
||||||
return &stateFile{}
|
|
||||||
})
|
|
||||||
|
|
||||||
errMsg := logData.String()
|
|
||||||
|
|
||||||
if tc.expErr != "" {
|
|
||||||
if errMsg != "" {
|
|
||||||
if errMsg != tc.expErr {
|
|
||||||
t.Errorf("UpdateStateFile() error = %v, wantErr %v", errMsg, tc.expErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("UpdateStateFile() error = nil, wantErr %v", tc.expErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if errMsg != "" {
|
|
||||||
t.Errorf("UpdateStateFile() error = %v, wantErr nil", errMsg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newFileState, err := NewFileState(sfilePath.Name(), "static", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("NewFileState() error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
AssertStateEqual(t, newFileState, tc.expectedState)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHelpersStateFile(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
description string
|
|
||||||
defaultCPUset cpuset.CPUSet
|
|
||||||
assignments map[string]map[string]cpuset.CPUSet
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
description: "one container",
|
|
||||||
defaultCPUset: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8),
|
|
||||||
assignments: map[string]map[string]cpuset.CPUSet{
|
|
||||||
"pod": {
|
|
||||||
"c1": cpuset.NewCPUSet(0, 1),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "two containers",
|
|
||||||
defaultCPUset: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8),
|
|
||||||
assignments: map[string]map[string]cpuset.CPUSet{
|
|
||||||
"pod": {
|
|
||||||
"c1": cpuset.NewCPUSet(0, 1),
|
|
||||||
"c2": cpuset.NewCPUSet(2, 3, 4, 5),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "container with more cpus than is possible",
|
|
||||||
defaultCPUset: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8),
|
|
||||||
assignments: map[string]map[string]cpuset.CPUSet{
|
|
||||||
"pod": {
|
|
||||||
"c1": cpuset.NewCPUSet(0, 10),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "container without assigned cpus",
|
|
||||||
defaultCPUset: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8),
|
|
||||||
assignments: map[string]map[string]cpuset.CPUSet{
|
|
||||||
"pod": {
|
|
||||||
"c1": cpuset.NewCPUSet(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.description, func(t *testing.T) {
|
|
||||||
sfFile, err := ioutil.TempFile("/tmp", "testHelpersStateFile")
|
|
||||||
defer os.Remove(sfFile.Name())
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("cannot create temporary test file: %q", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
state, err := NewFileState(sfFile.Name(), "static", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("new file state error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
state.SetDefaultCPUSet(tc.defaultCPUset)
|
|
||||||
|
|
||||||
for podUID := range tc.assignments {
|
|
||||||
for containerName, containerCPUs := range tc.assignments[podUID] {
|
|
||||||
state.SetCPUSet(podUID, containerName, containerCPUs)
|
|
||||||
if cpus, _ := state.GetCPUSet(podUID, containerName); !cpus.Equals(containerCPUs) {
|
|
||||||
t.Errorf("state is inconsistent. Wants = %q Have = %q", containerCPUs, cpus)
|
|
||||||
}
|
|
||||||
state.Delete(podUID, containerName)
|
|
||||||
if cpus := state.GetCPUSetOrDefault(podUID, containerName); !cpus.Equals(tc.defaultCPUset) {
|
|
||||||
t.Error("deleted container still existing in state")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClearStateStateFile(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
description string
|
|
||||||
defaultCPUset cpuset.CPUSet
|
|
||||||
assignments map[string]map[string]cpuset.CPUSet
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
description: "valid file",
|
|
||||||
defaultCPUset: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8),
|
|
||||||
assignments: map[string]map[string]cpuset.CPUSet{
|
|
||||||
"pod": {
|
|
||||||
"c1": cpuset.NewCPUSet(0, 1),
|
|
||||||
"c2": cpuset.NewCPUSet(2, 3),
|
|
||||||
"c3": cpuset.NewCPUSet(4, 5),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, testCase := range testCases {
|
|
||||||
t.Run(testCase.description, func(t *testing.T) {
|
|
||||||
sfFile, err := ioutil.TempFile("/tmp", "testHelpersStateFile")
|
|
||||||
defer os.Remove(sfFile.Name())
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("cannot create temporary test file: %q", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
state, err := NewFileState(sfFile.Name(), "static", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("new file state error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
state.SetDefaultCPUSet(testCase.defaultCPUset)
|
|
||||||
for podUID := range testCase.assignments {
|
|
||||||
for containerName, containerCPUs := range testCase.assignments[podUID] {
|
|
||||||
state.SetCPUSet(podUID, containerName, containerCPUs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.ClearState()
|
|
||||||
if !cpuset.NewCPUSet().Equals(state.GetDefaultCPUSet()) {
|
|
||||||
t.Error("cleared state shouldn't has got information about available cpuset")
|
|
||||||
}
|
|
||||||
for podUID := range testCase.assignments {
|
|
||||||
for containerName := range testCase.assignments[podUID] {
|
|
||||||
if !cpuset.NewCPUSet().Equals(state.GetCPUSetOrDefault(podUID, containerName)) {
|
|
||||||
t.Error("cleared state shouldn't has got information about containers")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user