mirror of https://github.com/k3s-io/k3s
Merge pull request #54408 from intelsdi-x/cpu-state-file
Automatic merge from submit-queue (batch tested with PRs 54656, 54552, 54389, 53634, 54408). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Add file backed state to cpu manager **What this PR does / why we need it**: Adds file backed `State` implementation to cpu manger with tests. Reads from `State` are done from memory, while each write triggers state save to a file. Any failure in reading the state file results in empty state Next PR: #54409pull/6/head
commit
94e77bd4ca
|
@ -32,7 +32,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockState struct {
|
type mockState struct {
|
||||||
assignments map[string]cpuset.CPUSet
|
assignments state.ContainerCPUAssignments
|
||||||
defaultCPUSet cpuset.CPUSet
|
defaultCPUSet cpuset.CPUSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +64,19 @@ func (s *mockState) Delete(containerID string) {
|
||||||
delete(s.assignments, containerID)
|
delete(s.assignments, containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *mockState) ClearState() {
|
||||||
|
s.defaultCPUSet = cpuset.CPUSet{}
|
||||||
|
s.assignments = make(state.ContainerCPUAssignments)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *mockState) SetCPUAssignments(a state.ContainerCPUAssignments) {
|
||||||
|
s.assignments = a.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *mockState) GetCPUAssignments() state.ContainerCPUAssignments {
|
||||||
|
return s.assignments.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
type mockPolicy struct {
|
type mockPolicy struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
@ -190,7 +203,7 @@ func TestCPUManagerAdd(t *testing.T) {
|
||||||
err: testCase.regErr,
|
err: testCase.regErr,
|
||||||
},
|
},
|
||||||
state: &mockState{
|
state: &mockState{
|
||||||
assignments: map[string]cpuset.CPUSet{},
|
assignments: state.ContainerCPUAssignments{},
|
||||||
defaultCPUSet: cpuset.NewCPUSet(),
|
defaultCPUSet: cpuset.NewCPUSet(),
|
||||||
},
|
},
|
||||||
containerRuntime: mockRuntimeService{
|
containerRuntime: mockRuntimeService{
|
||||||
|
@ -216,7 +229,7 @@ func TestCPUManagerRemove(t *testing.T) {
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
state: &mockState{
|
state: &mockState{
|
||||||
assignments: map[string]cpuset.CPUSet{},
|
assignments: state.ContainerCPUAssignments{},
|
||||||
defaultCPUSet: cpuset.NewCPUSet(),
|
defaultCPUSet: cpuset.NewCPUSet(),
|
||||||
},
|
},
|
||||||
containerRuntime: mockRuntimeService{},
|
containerRuntime: mockRuntimeService{},
|
||||||
|
@ -251,7 +264,7 @@ func TestReconcileState(t *testing.T) {
|
||||||
activePods []*v1.Pod
|
activePods []*v1.Pod
|
||||||
pspPS v1.PodStatus
|
pspPS v1.PodStatus
|
||||||
pspFound bool
|
pspFound bool
|
||||||
stAssignments map[string]cpuset.CPUSet
|
stAssignments state.ContainerCPUAssignments
|
||||||
stDefaultCPUSet cpuset.CPUSet
|
stDefaultCPUSet cpuset.CPUSet
|
||||||
updateErr error
|
updateErr error
|
||||||
expectFailedContainerName string
|
expectFailedContainerName string
|
||||||
|
@ -282,7 +295,7 @@ func TestReconcileState(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pspFound: true,
|
pspFound: true,
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID": cpuset.NewCPUSet(1, 2),
|
"fakeID": cpuset.NewCPUSet(1, 2),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(3, 4, 5, 6, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(3, 4, 5, 6, 7),
|
||||||
|
@ -308,7 +321,7 @@ func TestReconcileState(t *testing.T) {
|
||||||
},
|
},
|
||||||
pspPS: v1.PodStatus{},
|
pspPS: v1.PodStatus{},
|
||||||
pspFound: false,
|
pspFound: false,
|
||||||
stAssignments: map[string]cpuset.CPUSet{},
|
stAssignments: state.ContainerCPUAssignments{},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(),
|
stDefaultCPUSet: cpuset.NewCPUSet(),
|
||||||
updateErr: nil,
|
updateErr: nil,
|
||||||
expectFailedContainerName: "fakeName",
|
expectFailedContainerName: "fakeName",
|
||||||
|
@ -339,7 +352,7 @@ func TestReconcileState(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pspFound: true,
|
pspFound: true,
|
||||||
stAssignments: map[string]cpuset.CPUSet{},
|
stAssignments: state.ContainerCPUAssignments{},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(),
|
stDefaultCPUSet: cpuset.NewCPUSet(),
|
||||||
updateErr: nil,
|
updateErr: nil,
|
||||||
expectFailedContainerName: "fakeName",
|
expectFailedContainerName: "fakeName",
|
||||||
|
@ -370,7 +383,7 @@ func TestReconcileState(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pspFound: true,
|
pspFound: true,
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID": cpuset.NewCPUSet(),
|
"fakeID": cpuset.NewCPUSet(),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(1, 2, 3, 4, 5, 6, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(1, 2, 3, 4, 5, 6, 7),
|
||||||
|
@ -403,7 +416,7 @@ func TestReconcileState(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pspFound: true,
|
pspFound: true,
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID": cpuset.NewCPUSet(1, 2),
|
"fakeID": cpuset.NewCPUSet(1, 2),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(3, 4, 5, 6, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(3, 4, 5, 6, 7),
|
||||||
|
|
|
@ -19,6 +19,7 @@ package cpumanager
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ func TestNonePolicyAdd(t *testing.T) {
|
||||||
policy := &nonePolicy{}
|
policy := &nonePolicy{}
|
||||||
|
|
||||||
st := &mockState{
|
st := &mockState{
|
||||||
assignments: map[string]cpuset.CPUSet{},
|
assignments: state.ContainerCPUAssignments{},
|
||||||
defaultCPUSet: cpuset.NewCPUSet(1, 2, 3, 4, 5, 6, 7),
|
defaultCPUSet: cpuset.NewCPUSet(1, 2, 3, 4, 5, 6, 7),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ func TestNonePolicyRemove(t *testing.T) {
|
||||||
policy := &nonePolicy{}
|
policy := &nonePolicy{}
|
||||||
|
|
||||||
st := &mockState{
|
st := &mockState{
|
||||||
assignments: map[string]cpuset.CPUSet{},
|
assignments: state.ContainerCPUAssignments{},
|
||||||
defaultCPUSet: cpuset.NewCPUSet(1, 2, 3, 4, 5, 6, 7),
|
defaultCPUSet: cpuset.NewCPUSet(1, 2, 3, 4, 5, 6, 7),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology"
|
"k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
||||||
)
|
)
|
||||||
|
@ -31,7 +32,7 @@ type staticPolicyTest struct {
|
||||||
topo *topology.CPUTopology
|
topo *topology.CPUTopology
|
||||||
numReservedCPUs int
|
numReservedCPUs int
|
||||||
containerID string
|
containerID string
|
||||||
stAssignments map[string]cpuset.CPUSet
|
stAssignments state.ContainerCPUAssignments
|
||||||
stDefaultCPUSet cpuset.CPUSet
|
stDefaultCPUSet cpuset.CPUSet
|
||||||
pod *v1.Pod
|
pod *v1.Pod
|
||||||
expErr error
|
expErr error
|
||||||
|
@ -53,7 +54,7 @@ func TestStaticPolicyStart(t *testing.T) {
|
||||||
policy := NewStaticPolicy(topoSingleSocketHT, 1).(*staticPolicy)
|
policy := NewStaticPolicy(topoSingleSocketHT, 1).(*staticPolicy)
|
||||||
|
|
||||||
st := &mockState{
|
st := &mockState{
|
||||||
assignments: map[string]cpuset.CPUSet{},
|
assignments: state.ContainerCPUAssignments{},
|
||||||
defaultCPUSet: cpuset.NewCPUSet(),
|
defaultCPUSet: cpuset.NewCPUSet(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +89,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoSingleSocketHT,
|
topo: topoSingleSocketHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID2",
|
containerID: "fakeID2",
|
||||||
stAssignments: map[string]cpuset.CPUSet{},
|
stAssignments: state.ContainerCPUAssignments{},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
|
||||||
pod: makePod("8000m", "8000m"),
|
pod: makePod("8000m", "8000m"),
|
||||||
expErr: fmt.Errorf("not enough cpus available to satisfy request"),
|
expErr: fmt.Errorf("not enough cpus available to satisfy request"),
|
||||||
|
@ -100,7 +101,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoSingleSocketHT,
|
topo: topoSingleSocketHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID2",
|
containerID: "fakeID2",
|
||||||
stAssignments: map[string]cpuset.CPUSet{},
|
stAssignments: state.ContainerCPUAssignments{},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
|
||||||
pod: makePod("1000m", "1000m"),
|
pod: makePod("1000m", "1000m"),
|
||||||
expErr: nil,
|
expErr: nil,
|
||||||
|
@ -112,7 +113,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoSingleSocketHT,
|
topo: topoSingleSocketHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID3",
|
containerID: "fakeID3",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": cpuset.NewCPUSet(2, 3, 6, 7),
|
"fakeID100": cpuset.NewCPUSet(2, 3, 6, 7),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 4, 5),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 4, 5),
|
||||||
|
@ -126,7 +127,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoDualSocketHT,
|
topo: topoDualSocketHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID3",
|
containerID: "fakeID3",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": cpuset.NewCPUSet(2),
|
"fakeID100": cpuset.NewCPUSet(2),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11),
|
||||||
|
@ -140,7 +141,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoDualSocketHT,
|
topo: topoDualSocketHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID3",
|
containerID: "fakeID3",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": cpuset.NewCPUSet(1, 5),
|
"fakeID100": cpuset.NewCPUSet(1, 5),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 2, 3, 4, 6, 7, 8, 9, 10, 11),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 2, 3, 4, 6, 7, 8, 9, 10, 11),
|
||||||
|
@ -154,7 +155,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoDualSocketNoHT,
|
topo: topoDualSocketNoHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID1",
|
containerID: "fakeID1",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": cpuset.NewCPUSet(),
|
"fakeID100": cpuset.NewCPUSet(),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 3, 4, 5, 6, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 3, 4, 5, 6, 7),
|
||||||
|
@ -168,7 +169,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoDualSocketNoHT,
|
topo: topoDualSocketNoHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID1",
|
containerID: "fakeID1",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": cpuset.NewCPUSet(4, 5),
|
"fakeID100": cpuset.NewCPUSet(4, 5),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 3, 6, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 3, 6, 7),
|
||||||
|
@ -182,7 +183,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoDualSocketHT,
|
topo: topoDualSocketHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID3",
|
containerID: "fakeID3",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": cpuset.NewCPUSet(2),
|
"fakeID100": cpuset.NewCPUSet(2),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11),
|
||||||
|
@ -196,7 +197,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoSingleSocketHT,
|
topo: topoSingleSocketHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID1",
|
containerID: "fakeID1",
|
||||||
stAssignments: map[string]cpuset.CPUSet{},
|
stAssignments: state.ContainerCPUAssignments{},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
|
||||||
pod: makePod("1000m", "2000m"),
|
pod: makePod("1000m", "2000m"),
|
||||||
expErr: nil,
|
expErr: nil,
|
||||||
|
@ -208,7 +209,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoSingleSocketHT,
|
topo: topoSingleSocketHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID4",
|
containerID: "fakeID4",
|
||||||
stAssignments: map[string]cpuset.CPUSet{},
|
stAssignments: state.ContainerCPUAssignments{},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
|
||||||
pod: makePod("977m", "977m"),
|
pod: makePod("977m", "977m"),
|
||||||
expErr: nil,
|
expErr: nil,
|
||||||
|
@ -220,7 +221,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoSingleSocketHT,
|
topo: topoSingleSocketHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID5",
|
containerID: "fakeID5",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": cpuset.NewCPUSet(1, 2, 3, 4, 5, 6),
|
"fakeID100": cpuset.NewCPUSet(1, 2, 3, 4, 5, 6),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 7),
|
||||||
|
@ -234,7 +235,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
topo: topoDualSocketHT,
|
topo: topoDualSocketHT,
|
||||||
numReservedCPUs: 1,
|
numReservedCPUs: 1,
|
||||||
containerID: "fakeID5",
|
containerID: "fakeID5",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": cpuset.NewCPUSet(1, 2, 3),
|
"fakeID100": cpuset.NewCPUSet(1, 2, 3),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(0, 4, 5, 6, 7, 8, 9, 10, 11),
|
stDefaultCPUSet: cpuset.NewCPUSet(0, 4, 5, 6, 7, 8, 9, 10, 11),
|
||||||
|
@ -250,7 +251,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocSock0",
|
description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocSock0",
|
||||||
topo: topoQuadSocketFourWayHT,
|
topo: topoQuadSocketFourWayHT,
|
||||||
containerID: "fakeID5",
|
containerID: "fakeID5",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": cpuset.NewCPUSet(3, 11, 4, 5, 6, 7),
|
"fakeID100": cpuset.NewCPUSet(3, 11, 4, 5, 6, 7),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: largeTopoCPUSet.Difference(cpuset.NewCPUSet(3, 11, 4, 5, 6, 7)),
|
stDefaultCPUSet: largeTopoCPUSet.Difference(cpuset.NewCPUSet(3, 11, 4, 5, 6, 7)),
|
||||||
|
@ -265,7 +266,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocAllFullCoresFromThreeSockets",
|
description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocAllFullCoresFromThreeSockets",
|
||||||
topo: topoQuadSocketFourWayHT,
|
topo: topoQuadSocketFourWayHT,
|
||||||
containerID: "fakeID5",
|
containerID: "fakeID5",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": largeTopoCPUSet.Difference(cpuset.NewCPUSet(1, 25, 13, 38, 2, 9, 11, 35, 23, 48, 12, 51,
|
"fakeID100": largeTopoCPUSet.Difference(cpuset.NewCPUSet(1, 25, 13, 38, 2, 9, 11, 35, 23, 48, 12, 51,
|
||||||
53, 173, 113, 233, 54, 61)),
|
53, 173, 113, 233, 54, 61)),
|
||||||
},
|
},
|
||||||
|
@ -281,7 +282,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocAllSock1+FullCore",
|
description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocAllSock1+FullCore",
|
||||||
topo: topoQuadSocketFourWayHT,
|
topo: topoQuadSocketFourWayHT,
|
||||||
containerID: "fakeID5",
|
containerID: "fakeID5",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": largeTopoCPUSet.Difference(largeTopoSock1CPUSet.Union(cpuset.NewCPUSet(10, 34, 22, 47, 53,
|
"fakeID100": largeTopoCPUSet.Difference(largeTopoSock1CPUSet.Union(cpuset.NewCPUSet(10, 34, 22, 47, 53,
|
||||||
173, 61, 181, 108, 228, 115, 235))),
|
173, 61, 181, 108, 228, 115, 235))),
|
||||||
},
|
},
|
||||||
|
@ -298,7 +299,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocCPUs",
|
description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocCPUs",
|
||||||
topo: topoQuadSocketFourWayHT,
|
topo: topoQuadSocketFourWayHT,
|
||||||
containerID: "fakeID5",
|
containerID: "fakeID5",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": largeTopoCPUSet.Difference(cpuset.NewCPUSet(10, 11, 53, 37, 55, 67, 52)),
|
"fakeID100": largeTopoCPUSet.Difference(cpuset.NewCPUSet(10, 11, 53, 37, 55, 67, 52)),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(10, 11, 53, 67, 52),
|
stDefaultCPUSet: cpuset.NewCPUSet(10, 11, 53, 67, 52),
|
||||||
|
@ -314,7 +315,7 @@ func TestStaticPolicyAdd(t *testing.T) {
|
||||||
description: "GuPodMultipleCores, topoQuadSocketFourWayHT, NoAlloc",
|
description: "GuPodMultipleCores, topoQuadSocketFourWayHT, NoAlloc",
|
||||||
topo: topoQuadSocketFourWayHT,
|
topo: topoQuadSocketFourWayHT,
|
||||||
containerID: "fakeID5",
|
containerID: "fakeID5",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID100": largeTopoCPUSet.Difference(cpuset.NewCPUSet(10, 11, 53, 37, 55, 67, 52)),
|
"fakeID100": largeTopoCPUSet.Difference(cpuset.NewCPUSet(10, 11, 53, 37, 55, 67, 52)),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(10, 11, 53, 37, 55, 67, 52),
|
stDefaultCPUSet: cpuset.NewCPUSet(10, 11, 53, 37, 55, 67, 52),
|
||||||
|
@ -374,7 +375,7 @@ func TestStaticPolicyRemove(t *testing.T) {
|
||||||
description: "SingleSocketHT, DeAllocOneContainer",
|
description: "SingleSocketHT, DeAllocOneContainer",
|
||||||
topo: topoSingleSocketHT,
|
topo: topoSingleSocketHT,
|
||||||
containerID: "fakeID1",
|
containerID: "fakeID1",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID1": cpuset.NewCPUSet(1, 2, 3),
|
"fakeID1": cpuset.NewCPUSet(1, 2, 3),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(4, 5, 6, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(4, 5, 6, 7),
|
||||||
|
@ -384,7 +385,7 @@ func TestStaticPolicyRemove(t *testing.T) {
|
||||||
description: "SingleSocketHT, DeAllocOneContainer, BeginEmpty",
|
description: "SingleSocketHT, DeAllocOneContainer, BeginEmpty",
|
||||||
topo: topoSingleSocketHT,
|
topo: topoSingleSocketHT,
|
||||||
containerID: "fakeID1",
|
containerID: "fakeID1",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID1": cpuset.NewCPUSet(1, 2, 3),
|
"fakeID1": cpuset.NewCPUSet(1, 2, 3),
|
||||||
"fakeID2": cpuset.NewCPUSet(4, 5, 6, 7),
|
"fakeID2": cpuset.NewCPUSet(4, 5, 6, 7),
|
||||||
},
|
},
|
||||||
|
@ -395,7 +396,7 @@ func TestStaticPolicyRemove(t *testing.T) {
|
||||||
description: "SingleSocketHT, DeAllocTwoContainer",
|
description: "SingleSocketHT, DeAllocTwoContainer",
|
||||||
topo: topoSingleSocketHT,
|
topo: topoSingleSocketHT,
|
||||||
containerID: "fakeID1",
|
containerID: "fakeID1",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID1": cpuset.NewCPUSet(1, 3, 5),
|
"fakeID1": cpuset.NewCPUSet(1, 3, 5),
|
||||||
"fakeID2": cpuset.NewCPUSet(2, 4),
|
"fakeID2": cpuset.NewCPUSet(2, 4),
|
||||||
},
|
},
|
||||||
|
@ -406,7 +407,7 @@ func TestStaticPolicyRemove(t *testing.T) {
|
||||||
description: "SingleSocketHT, NoDeAlloc",
|
description: "SingleSocketHT, NoDeAlloc",
|
||||||
topo: topoSingleSocketHT,
|
topo: topoSingleSocketHT,
|
||||||
containerID: "fakeID2",
|
containerID: "fakeID2",
|
||||||
stAssignments: map[string]cpuset.CPUSet{
|
stAssignments: state.ContainerCPUAssignments{
|
||||||
"fakeID1": cpuset.NewCPUSet(1, 3, 5),
|
"fakeID1": cpuset.NewCPUSet(1, 3, 5),
|
||||||
},
|
},
|
||||||
stDefaultCPUSet: cpuset.NewCPUSet(2, 4, 6, 7),
|
stDefaultCPUSet: cpuset.NewCPUSet(2, 4, 6, 7),
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
"state.go",
|
"state.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",
|
||||||
|
@ -14,6 +15,14 @@ go_library(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["state_file_test.go"],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state",
|
||||||
|
library = ":go_default_library",
|
||||||
|
deps = ["//pkg/kubelet/cm/cpuset:go_default_library"],
|
||||||
|
)
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "package-srcs",
|
name = "package-srcs",
|
||||||
srcs = glob(["**"]),
|
srcs = glob(["**"]),
|
||||||
|
|
|
@ -20,17 +20,32 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ContainerCPUAssignments type used in cpu manger state
|
||||||
|
type ContainerCPUAssignments map[string]cpuset.CPUSet
|
||||||
|
|
||||||
|
// Clone returns a copy of ContainerCPUAssignments
|
||||||
|
func (as ContainerCPUAssignments) Clone() ContainerCPUAssignments {
|
||||||
|
ret := make(ContainerCPUAssignments)
|
||||||
|
for key, val := range as {
|
||||||
|
ret[key] = val
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
// Reader interface used to read current cpu/pod assignment state
|
// Reader interface used to read current cpu/pod assignment state
|
||||||
type Reader interface {
|
type Reader interface {
|
||||||
GetCPUSet(containerID string) (cpuset.CPUSet, bool)
|
GetCPUSet(containerID string) (cpuset.CPUSet, bool)
|
||||||
GetDefaultCPUSet() cpuset.CPUSet
|
GetDefaultCPUSet() cpuset.CPUSet
|
||||||
GetCPUSetOrDefault(containerID string) cpuset.CPUSet
|
GetCPUSetOrDefault(containerID string) cpuset.CPUSet
|
||||||
|
GetCPUAssignments() ContainerCPUAssignments
|
||||||
}
|
}
|
||||||
|
|
||||||
type writer interface {
|
type writer interface {
|
||||||
SetCPUSet(containerID string, cpuset cpuset.CPUSet)
|
SetCPUSet(containerID string, cpuset cpuset.CPUSet)
|
||||||
SetDefaultCPUSet(cpuset cpuset.CPUSet)
|
SetDefaultCPUSet(cpuset cpuset.CPUSet)
|
||||||
|
SetCPUAssignments(ContainerCPUAssignments)
|
||||||
Delete(containerID string)
|
Delete(containerID string)
|
||||||
|
ClearState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// State interface provides methods for tracking and setting cpu/pod assignment
|
// State interface provides methods for tracking and setting cpu/pod assignment
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
/*
|
||||||
|
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"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"io/ioutil"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stateFileData struct {
|
||||||
|
DefaultCPUSet string `json:"defaultCpuSet"`
|
||||||
|
Entries map[string]string `json:"entries,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ State = &stateFile{}
|
||||||
|
|
||||||
|
type stateFile struct {
|
||||||
|
sync.RWMutex
|
||||||
|
stateFilePath string
|
||||||
|
cache State
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileState creates new State for keeping track of cpu/pod assignment with file backend
|
||||||
|
func NewFileState(filePath string) State {
|
||||||
|
stateFile := &stateFile{
|
||||||
|
stateFilePath: filePath,
|
||||||
|
cache: NewMemoryState(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := stateFile.tryRestoreState(); err != nil {
|
||||||
|
// could not restore state, init new state file
|
||||||
|
glog.Infof("[cpumanager] state file: initializing empty state file")
|
||||||
|
stateFile.cache.ClearState()
|
||||||
|
stateFile.storeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// used when all parsing is ok
|
||||||
|
tmpAssignments := make(ContainerCPUAssignments)
|
||||||
|
tmpDefaultCPUSet := cpuset.NewCPUSet()
|
||||||
|
tmpContainerCPUSet := cpuset.NewCPUSet()
|
||||||
|
|
||||||
|
var content []byte
|
||||||
|
|
||||||
|
if content, err = ioutil.ReadFile(sf.stateFilePath); os.IsNotExist(err) {
|
||||||
|
// Create file
|
||||||
|
if _, err = os.Create(sf.stateFilePath); err != nil {
|
||||||
|
glog.Errorf("[cpumanager] state file: unable to create state file \"%s\":%s", sf.stateFilePath, err.Error())
|
||||||
|
panic("[cpumanager] state file not created")
|
||||||
|
}
|
||||||
|
glog.Infof("[cpumanager] state file: created empty state file \"%s\"", sf.stateFilePath)
|
||||||
|
} else {
|
||||||
|
// File exists - try to read
|
||||||
|
var readState stateFileData
|
||||||
|
|
||||||
|
if err = json.Unmarshal(content, &readState); err != nil {
|
||||||
|
glog.Warningf("[cpumanager] state file: could not unmarshal, corrupted state file - \"%s\"", sf.stateFilePath)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpDefaultCPUSet, err = cpuset.Parse(readState.DefaultCPUSet); err != nil {
|
||||||
|
glog.Warningf("[cpumanager] state file: could not parse state file - [defaultCpuSet:\"%s\"]", readState.DefaultCPUSet)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for containerID, cpuString := range readState.Entries {
|
||||||
|
if tmpContainerCPUSet, err = cpuset.Parse(cpuString); err != nil {
|
||||||
|
glog.Warningf("[cpumanager] state file: could not parse state file - container id: %s, cpuset: \"%s\"", containerID, cpuString)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tmpAssignments[containerID] = tmpContainerCPUSet
|
||||||
|
}
|
||||||
|
|
||||||
|
sf.cache.SetDefaultCPUSet(tmpDefaultCPUSet)
|
||||||
|
sf.cache.SetCPUAssignments(tmpAssignments)
|
||||||
|
|
||||||
|
glog.V(2).Infof("[cpumanager] state file: restored state from state file \"%s\"", sf.stateFilePath)
|
||||||
|
glog.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() {
|
||||||
|
var content []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
data := stateFileData{
|
||||||
|
DefaultCPUSet: sf.cache.GetDefaultCPUSet().String(),
|
||||||
|
Entries: map[string]string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for containerID, cset := range sf.cache.GetCPUAssignments() {
|
||||||
|
data.Entries[containerID] = cset.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if content, err = json.Marshal(data); err != nil {
|
||||||
|
panic("[cpumanager] state file: could not serialize state to json")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ioutil.WriteFile(sf.stateFilePath, content, 0644); err != nil {
|
||||||
|
panic("[cpumanager] state file not written")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *stateFile) GetCPUSet(containerID string) (cpuset.CPUSet, bool) {
|
||||||
|
sf.RLock()
|
||||||
|
defer sf.RUnlock()
|
||||||
|
|
||||||
|
res, ok := sf.cache.GetCPUSet(containerID)
|
||||||
|
return res, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *stateFile) GetDefaultCPUSet() cpuset.CPUSet {
|
||||||
|
sf.RLock()
|
||||||
|
defer sf.RUnlock()
|
||||||
|
|
||||||
|
return sf.cache.GetDefaultCPUSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *stateFile) GetCPUSetOrDefault(containerID string) cpuset.CPUSet {
|
||||||
|
sf.RLock()
|
||||||
|
defer sf.RUnlock()
|
||||||
|
|
||||||
|
return sf.cache.GetCPUSetOrDefault(containerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *stateFile) GetCPUAssignments() ContainerCPUAssignments {
|
||||||
|
sf.RLock()
|
||||||
|
defer sf.RUnlock()
|
||||||
|
return sf.cache.GetCPUAssignments()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *stateFile) SetCPUSet(containerID string, cset cpuset.CPUSet) {
|
||||||
|
sf.Lock()
|
||||||
|
defer sf.Unlock()
|
||||||
|
sf.cache.SetCPUSet(containerID, cset)
|
||||||
|
sf.storeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *stateFile) SetDefaultCPUSet(cset cpuset.CPUSet) {
|
||||||
|
sf.Lock()
|
||||||
|
defer sf.Unlock()
|
||||||
|
sf.cache.SetDefaultCPUSet(cset)
|
||||||
|
sf.storeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *stateFile) SetCPUAssignments(a ContainerCPUAssignments) {
|
||||||
|
sf.Lock()
|
||||||
|
defer sf.Unlock()
|
||||||
|
sf.cache.SetCPUAssignments(a)
|
||||||
|
sf.storeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *stateFile) Delete(containerID string) {
|
||||||
|
sf.Lock()
|
||||||
|
defer sf.Unlock()
|
||||||
|
sf.cache.Delete(containerID)
|
||||||
|
sf.storeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *stateFile) ClearState() {
|
||||||
|
sf.Lock()
|
||||||
|
defer sf.Unlock()
|
||||||
|
sf.cache.ClearState()
|
||||||
|
sf.storeState()
|
||||||
|
}
|
|
@ -0,0 +1,446 @@
|
||||||
|
/*
|
||||||
|
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"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeToStateFile(statefile string, content string) {
|
||||||
|
ioutil.WriteFile(statefile, []byte(content), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stateEqual(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 assigments 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) {
|
||||||
|
flag.Set("alsologtostderr", "true")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
description string
|
||||||
|
stateFileContent string
|
||||||
|
expErr string
|
||||||
|
expectedState *stateMemory
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Invalid JSON - empty file",
|
||||||
|
"\n",
|
||||||
|
"state file: could not unmarshal, corrupted state file",
|
||||||
|
&stateMemory{
|
||||||
|
assignments: ContainerCPUAssignments{},
|
||||||
|
defaultCPUSet: cpuset.NewCPUSet(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Invalid JSON - invalid content",
|
||||||
|
"{",
|
||||||
|
"state file: could not unmarshal, corrupted state file",
|
||||||
|
&stateMemory{
|
||||||
|
assignments: ContainerCPUAssignments{},
|
||||||
|
defaultCPUSet: cpuset.NewCPUSet(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Try restore defaultCPUSet only",
|
||||||
|
"{ \"defaultCpuSet\": \"4-6\"}",
|
||||||
|
"",
|
||||||
|
&stateMemory{
|
||||||
|
assignments: ContainerCPUAssignments{},
|
||||||
|
defaultCPUSet: cpuset.NewCPUSet(4, 5, 6),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Try restore defaultCPUSet only - invalid name",
|
||||||
|
"{ \"defCPUSet\": \"4-6\"}",
|
||||||
|
"",
|
||||||
|
&stateMemory{
|
||||||
|
assignments: ContainerCPUAssignments{},
|
||||||
|
defaultCPUSet: cpuset.NewCPUSet(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Try restore assignments only",
|
||||||
|
"{" +
|
||||||
|
"\"entries\": { " +
|
||||||
|
"\"container1\": \"4-6\"," +
|
||||||
|
"\"container2\": \"1-3\"" +
|
||||||
|
"} }",
|
||||||
|
"",
|
||||||
|
&stateMemory{
|
||||||
|
assignments: ContainerCPUAssignments{
|
||||||
|
"container1": cpuset.NewCPUSet(4, 5, 6),
|
||||||
|
"container2": cpuset.NewCPUSet(1, 2, 3),
|
||||||
|
},
|
||||||
|
defaultCPUSet: cpuset.NewCPUSet(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Try restore invalid assignments",
|
||||||
|
"{ \"entries\": }",
|
||||||
|
"state file: could not unmarshal, corrupted state file",
|
||||||
|
&stateMemory{
|
||||||
|
assignments: ContainerCPUAssignments{},
|
||||||
|
defaultCPUSet: cpuset.NewCPUSet(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Try restore valid file",
|
||||||
|
"{ " +
|
||||||
|
"\"defaultCpuSet\": \"23-24\", " +
|
||||||
|
"\"entries\": { " +
|
||||||
|
"\"container1\": \"4-6\", " +
|
||||||
|
"\"container2\": \"1-3\"" +
|
||||||
|
" } }",
|
||||||
|
"",
|
||||||
|
&stateMemory{
|
||||||
|
assignments: ContainerCPUAssignments{
|
||||||
|
"container1": cpuset.NewCPUSet(4, 5, 6),
|
||||||
|
"container2": cpuset.NewCPUSet(1, 2, 3),
|
||||||
|
},
|
||||||
|
defaultCPUSet: cpuset.NewCPUSet(23, 24),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Try restore un-parsable defaultCPUSet ",
|
||||||
|
"{ \"defaultCpuSet\": \"2-sd\" }",
|
||||||
|
"state file: could not parse state file",
|
||||||
|
&stateMemory{
|
||||||
|
assignments: ContainerCPUAssignments{},
|
||||||
|
defaultCPUSet: cpuset.NewCPUSet(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Try restore un-parsable assignments",
|
||||||
|
"{ " +
|
||||||
|
"\"defaultCpuSet\": \"23-24\", " +
|
||||||
|
"\"entries\": { " +
|
||||||
|
"\"container1\": \"p-6\", " +
|
||||||
|
"\"container2\": \"1-3\"" +
|
||||||
|
" } }",
|
||||||
|
"state file: could not parse state file",
|
||||||
|
&stateMemory{
|
||||||
|
assignments: ContainerCPUAssignments{},
|
||||||
|
defaultCPUSet: cpuset.NewCPUSet(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"TryRestoreState creates empty state file",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
&stateMemory{
|
||||||
|
assignments: ContainerCPUAssignments{},
|
||||||
|
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))
|
||||||
|
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 {
|
||||||
|
return NewFileState(sfilePath.Name())
|
||||||
|
})
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stateEqual(t, fileState, tc.expectedState)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileStateTryRestorePanic(t *testing.T) {
|
||||||
|
|
||||||
|
testCase := struct {
|
||||||
|
description string
|
||||||
|
wantPanic bool
|
||||||
|
panicMessage string
|
||||||
|
}{
|
||||||
|
"Panic creating file",
|
||||||
|
true,
|
||||||
|
"[cpumanager] state file not created",
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run(testCase.description, func(t *testing.T) {
|
||||||
|
sfilePath := path.Join("/invalid_path/to_some_dir", "cpumanager_state_file_test")
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
if testCase.wantPanic {
|
||||||
|
if testCase.panicMessage == err {
|
||||||
|
t.Logf("TryRestoreState() got expected panic = %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Errorf("TryRestoreState() unexpected panic = %v, wantErr %v", err, testCase.panicMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
NewFileState(sfilePath)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateStateFile(t *testing.T) {
|
||||||
|
flag.Set("alsologtostderr", "true")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
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{
|
||||||
|
"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(),
|
||||||
|
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 := NewFileState(sfilePath.Name())
|
||||||
|
stateEqual(t, newFileState, tc.expectedState)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHelpersStateFile(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
description string
|
||||||
|
defaultCPUset cpuset.CPUSet
|
||||||
|
containers map[string]cpuset.CPUSet
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
description: "one container",
|
||||||
|
defaultCPUset: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8),
|
||||||
|
containers: map[string]cpuset.CPUSet{
|
||||||
|
"c1": cpuset.NewCPUSet(0, 1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "two containers",
|
||||||
|
defaultCPUset: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8),
|
||||||
|
containers: map[string]cpuset.CPUSet{
|
||||||
|
"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),
|
||||||
|
containers: map[string]cpuset.CPUSet{
|
||||||
|
"c1": cpuset.NewCPUSet(0, 10),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "container without assigned cpus",
|
||||||
|
defaultCPUset: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8),
|
||||||
|
containers: map[string]cpuset.CPUSet{
|
||||||
|
"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 := NewFileState(sfFile.Name())
|
||||||
|
state.SetDefaultCPUSet(tc.defaultCPUset)
|
||||||
|
|
||||||
|
for containerName, containerCPUs := range tc.containers {
|
||||||
|
state.SetCPUSet(containerName, containerCPUs)
|
||||||
|
if cpus, _ := state.GetCPUSet(containerName); !cpus.Equals(containerCPUs) {
|
||||||
|
t.Errorf("state is inconsistant. Wants = %q Have = %q", containerCPUs, cpus)
|
||||||
|
}
|
||||||
|
state.Delete(containerName)
|
||||||
|
if cpus := state.GetCPUSetOrDefault(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
|
||||||
|
containers map[string]cpuset.CPUSet
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
description: "valid file",
|
||||||
|
defaultCPUset: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8),
|
||||||
|
containers: map[string]cpuset.CPUSet{
|
||||||
|
"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 := NewFileState(sfFile.Name())
|
||||||
|
state.SetDefaultCPUSet(testCase.defaultCPUset)
|
||||||
|
for containerName, containerCPUs := range testCase.containers {
|
||||||
|
state.SetCPUSet(containerName, containerCPUs)
|
||||||
|
}
|
||||||
|
|
||||||
|
state.ClearState()
|
||||||
|
if !cpuset.NewCPUSet().Equals(state.GetDefaultCPUSet()) {
|
||||||
|
t.Error("cleared state shoudn't has got information about available cpuset")
|
||||||
|
}
|
||||||
|
for containerName := range testCase.containers {
|
||||||
|
if !cpuset.NewCPUSet().Equals(state.GetCPUSetOrDefault(containerName)) {
|
||||||
|
t.Error("cleared state shoudn't has got information about containers")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ import (
|
||||||
|
|
||||||
type stateMemory struct {
|
type stateMemory struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
assignments map[string]cpuset.CPUSet
|
assignments ContainerCPUAssignments
|
||||||
defaultCPUSet cpuset.CPUSet
|
defaultCPUSet cpuset.CPUSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ var _ State = &stateMemory{}
|
||||||
func NewMemoryState() State {
|
func NewMemoryState() State {
|
||||||
glog.Infof("[cpumanager] initializing new in-memory state store")
|
glog.Infof("[cpumanager] initializing new in-memory state store")
|
||||||
return &stateMemory{
|
return &stateMemory{
|
||||||
assignments: map[string]cpuset.CPUSet{},
|
assignments: ContainerCPUAssignments{},
|
||||||
defaultCPUSet: cpuset.NewCPUSet(),
|
defaultCPUSet: cpuset.NewCPUSet(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,12 @@ func (s *stateMemory) GetCPUSetOrDefault(containerID string) cpuset.CPUSet {
|
||||||
return s.GetDefaultCPUSet()
|
return s.GetDefaultCPUSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stateMemory) GetCPUAssignments() ContainerCPUAssignments {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
return s.assignments.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stateMemory) SetCPUSet(containerID string, cset cpuset.CPUSet) {
|
func (s *stateMemory) SetCPUSet(containerID string, cset cpuset.CPUSet) {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
@ -81,6 +87,14 @@ func (s *stateMemory) SetDefaultCPUSet(cset cpuset.CPUSet) {
|
||||||
glog.Infof("[cpumanager] updated default cpuset: \"%s\"", cset)
|
glog.Infof("[cpumanager] updated default cpuset: \"%s\"", cset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stateMemory) SetCPUAssignments(a ContainerCPUAssignments) {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
s.assignments = a.Clone()
|
||||||
|
glog.Infof("[cpumanager] updated cpuset assignments: \"%v\"", a)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stateMemory) Delete(containerID string) {
|
func (s *stateMemory) Delete(containerID string) {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
@ -88,3 +102,12 @@ func (s *stateMemory) Delete(containerID string) {
|
||||||
delete(s.assignments, containerID)
|
delete(s.assignments, containerID)
|
||||||
glog.V(2).Infof("[cpumanager] deleted cpuset assignment (container id: %s)", containerID)
|
glog.V(2).Infof("[cpumanager] deleted cpuset assignment (container id: %s)", containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stateMemory) ClearState() {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
s.defaultCPUSet = cpuset.CPUSet{}
|
||||||
|
s.assignments = make(ContainerCPUAssignments)
|
||||||
|
glog.V(2).Infof("[cpumanager] cleared state")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue