...

Source file src/k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state/state_checkpoint_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package state
    18  
    19  import (
    20  	"os"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/require"
    26  	"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
    27  	"k8s.io/kubernetes/pkg/kubelet/cm/containermap"
    28  	testutil "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state/testing"
    29  	"k8s.io/utils/cpuset"
    30  )
    31  
    32  const testingCheckpoint = "cpumanager_checkpoint_test"
    33  
    34  func TestCheckpointStateRestore(t *testing.T) {
    35  	testCases := []struct {
    36  		description       string
    37  		checkpointContent string
    38  		policyName        string
    39  		initialContainers containermap.ContainerMap
    40  		expectedError     string
    41  		expectedState     *stateMemory
    42  	}{
    43  		{
    44  			"Restore non-existing checkpoint",
    45  			"",
    46  			"none",
    47  			containermap.ContainerMap{},
    48  			"",
    49  			&stateMemory{},
    50  		},
    51  		{
    52  			"Restore default cpu set",
    53  			`{
    54  				"policyName": "none",
    55  				"defaultCPUSet": "4-6",
    56  				"entries": {},
    57  				"checksum": 354655845
    58  			}`,
    59  			"none",
    60  			containermap.ContainerMap{},
    61  			"",
    62  			&stateMemory{
    63  				defaultCPUSet: cpuset.New(4, 5, 6),
    64  			},
    65  		},
    66  		{
    67  			"Restore valid checkpoint",
    68  			`{
    69  				"policyName": "none",
    70  				"defaultCPUSet": "1-3",
    71  				"entries": {
    72  					"pod": {
    73  						"container1": "4-6",
    74  						"container2": "1-3"
    75  					}
    76  				},
    77  				"checksum": 3610638499
    78  			}`,
    79  			"none",
    80  			containermap.ContainerMap{},
    81  			"",
    82  			&stateMemory{
    83  				assignments: ContainerCPUAssignments{
    84  					"pod": map[string]cpuset.CPUSet{
    85  						"container1": cpuset.New(4, 5, 6),
    86  						"container2": cpuset.New(1, 2, 3),
    87  					},
    88  				},
    89  				defaultCPUSet: cpuset.New(1, 2, 3),
    90  			},
    91  		},
    92  		{
    93  			"Restore checkpoint with invalid checksum",
    94  			`{
    95  				"policyName": "none",
    96  				"defaultCPUSet": "4-6",
    97  				"entries": {},
    98  				"checksum": 1337
    99  			}`,
   100  			"none",
   101  			containermap.ContainerMap{},
   102  			"checkpoint is corrupted",
   103  			&stateMemory{},
   104  		},
   105  		{
   106  			"Restore checkpoint with invalid JSON",
   107  			`{`,
   108  			"none",
   109  			containermap.ContainerMap{},
   110  			"unexpected end of JSON input",
   111  			&stateMemory{},
   112  		},
   113  		{
   114  			"Restore checkpoint with invalid policy name",
   115  			`{
   116  				"policyName": "other",
   117  				"defaultCPUSet": "1-3",
   118  				"entries": {},
   119  				"checksum": 1394507217
   120  			}`,
   121  			"none",
   122  			containermap.ContainerMap{},
   123  			`configured policy "none" differs from state checkpoint policy "other"`,
   124  			&stateMemory{},
   125  		},
   126  		{
   127  			"Restore checkpoint with unparsable default cpu set",
   128  			`{
   129  				"policyName": "none",
   130  				"defaultCPUSet": "1.3",
   131  				"entries": {},
   132  				"checksum": 3021697696
   133  			}`,
   134  			"none",
   135  			containermap.ContainerMap{},
   136  			`could not parse default cpu set "1.3": strconv.Atoi: parsing "1.3": invalid syntax`,
   137  			&stateMemory{},
   138  		},
   139  		{
   140  			"Restore checkpoint with unparsable assignment entry",
   141  			`{
   142  				"policyName": "none",
   143  				"defaultCPUSet": "1-3",
   144  				"entries": {
   145  					"pod": {
   146  						"container1": "4-6",
   147  						"container2": "asd"
   148  					}
   149  				},
   150  				"checksum": 962272150
   151  			}`,
   152  			"none",
   153  			containermap.ContainerMap{},
   154  			`could not parse cpuset "asd" for container "container2" in pod "pod": strconv.Atoi: parsing "asd": invalid syntax`,
   155  			&stateMemory{},
   156  		},
   157  		{
   158  			"Restore checkpoint from checkpoint with v1 checksum",
   159  			`{
   160  				"policyName": "none",
   161  				"defaultCPUSet": "1-3",
   162  				"checksum": 1694838852
   163  			}`,
   164  			"none",
   165  			containermap.ContainerMap{},
   166  			"",
   167  			&stateMemory{
   168  				defaultCPUSet: cpuset.New(1, 2, 3),
   169  			},
   170  		},
   171  		{
   172  			"Restore checkpoint with migration",
   173  			`{
   174  				"policyName": "none",
   175  				"defaultCPUSet": "1-3",
   176  				"entries": {
   177  					"containerID1": "4-6",
   178  					"containerID2": "1-3"
   179  				},
   180  				"checksum": 3680390589
   181  			}`,
   182  			"none",
   183  			func() containermap.ContainerMap {
   184  				cm := containermap.NewContainerMap()
   185  				cm.Add("pod", "container1", "containerID1")
   186  				cm.Add("pod", "container2", "containerID2")
   187  				return cm
   188  			}(),
   189  			"",
   190  			&stateMemory{
   191  				assignments: ContainerCPUAssignments{
   192  					"pod": map[string]cpuset.CPUSet{
   193  						"container1": cpuset.New(4, 5, 6),
   194  						"container2": cpuset.New(1, 2, 3),
   195  					},
   196  				},
   197  				defaultCPUSet: cpuset.New(1, 2, 3),
   198  			},
   199  		},
   200  	}
   201  
   202  	// create temp dir
   203  	testingDir, err := os.MkdirTemp("", "cpumanager_state_test")
   204  	require.NoError(t, err)
   205  	defer os.RemoveAll(testingDir)
   206  	// create checkpoint manager for testing
   207  	cpm, err := checkpointmanager.NewCheckpointManager(testingDir)
   208  	require.NoErrorf(t, err, "could not create testing checkpoint manager: %v", err)
   209  
   210  	for _, tc := range testCases {
   211  		t.Run(tc.description, func(t *testing.T) {
   212  			// ensure there is no previous checkpoint
   213  			cpm.RemoveCheckpoint(testingCheckpoint)
   214  
   215  			// prepare checkpoint for testing
   216  			if strings.TrimSpace(tc.checkpointContent) != "" {
   217  				checkpoint := &testutil.MockCheckpoint{Content: tc.checkpointContent}
   218  				err = cpm.CreateCheckpoint(testingCheckpoint, checkpoint)
   219  				require.NoErrorf(t, err, "could not create testing checkpoint: %v", err)
   220  			}
   221  
   222  			restoredState, err := NewCheckpointState(testingDir, testingCheckpoint, tc.policyName, tc.initialContainers)
   223  			if strings.TrimSpace(tc.expectedError) == "" {
   224  				require.NoError(t, err)
   225  			} else {
   226  				require.Error(t, err)
   227  				require.Contains(t, err.Error(), "could not restore state from checkpoint")
   228  				require.Contains(t, err.Error(), tc.expectedError)
   229  				return
   230  			}
   231  
   232  			// compare state after restoration with the one expected
   233  			AssertStateEqual(t, restoredState, tc.expectedState)
   234  		})
   235  	}
   236  }
   237  
   238  func TestCheckpointStateStore(t *testing.T) {
   239  	testCases := []struct {
   240  		description   string
   241  		expectedState *stateMemory
   242  	}{
   243  		{
   244  			"Store default cpu set",
   245  			&stateMemory{defaultCPUSet: cpuset.New(1, 2, 3)},
   246  		},
   247  		{
   248  			"Store assignments",
   249  			&stateMemory{
   250  				assignments: map[string]map[string]cpuset.CPUSet{
   251  					"pod": {
   252  						"container1": cpuset.New(1, 5, 8),
   253  					},
   254  				},
   255  			},
   256  		},
   257  	}
   258  
   259  	// create temp dir
   260  	testingDir, err := os.MkdirTemp("", "cpumanager_state_test")
   261  	if err != nil {
   262  		t.Fatal(err)
   263  	}
   264  	defer os.RemoveAll(testingDir)
   265  
   266  	cpm, err := checkpointmanager.NewCheckpointManager(testingDir)
   267  	if err != nil {
   268  		t.Fatalf("could not create testing checkpoint manager: %v", err)
   269  	}
   270  
   271  	for _, tc := range testCases {
   272  		t.Run(tc.description, func(t *testing.T) {
   273  			// ensure there is no previous checkpoint
   274  			cpm.RemoveCheckpoint(testingCheckpoint)
   275  
   276  			cs1, err := NewCheckpointState(testingDir, testingCheckpoint, "none", nil)
   277  			if err != nil {
   278  				t.Fatalf("could not create testing checkpointState instance: %v", err)
   279  			}
   280  
   281  			// set values of cs1 instance so they are stored in checkpoint and can be read by cs2
   282  			cs1.SetDefaultCPUSet(tc.expectedState.defaultCPUSet)
   283  			cs1.SetCPUAssignments(tc.expectedState.assignments)
   284  
   285  			// restore checkpoint with previously stored values
   286  			cs2, err := NewCheckpointState(testingDir, testingCheckpoint, "none", nil)
   287  			if err != nil {
   288  				t.Fatalf("could not create testing checkpointState instance: %v", err)
   289  			}
   290  
   291  			AssertStateEqual(t, cs2, tc.expectedState)
   292  		})
   293  	}
   294  }
   295  
   296  func TestCheckpointStateHelpers(t *testing.T) {
   297  	testCases := []struct {
   298  		description   string
   299  		defaultCPUset cpuset.CPUSet
   300  		assignments   map[string]map[string]cpuset.CPUSet
   301  	}{
   302  		{
   303  			description:   "One container",
   304  			defaultCPUset: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8),
   305  			assignments: map[string]map[string]cpuset.CPUSet{
   306  				"pod": {
   307  					"c1": cpuset.New(0, 1),
   308  				},
   309  			},
   310  		},
   311  		{
   312  			description:   "Two containers",
   313  			defaultCPUset: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8),
   314  			assignments: map[string]map[string]cpuset.CPUSet{
   315  				"pod": {
   316  					"c1": cpuset.New(0, 1),
   317  					"c2": cpuset.New(2, 3, 4, 5),
   318  				},
   319  			},
   320  		},
   321  		{
   322  			description:   "Container without assigned cpus",
   323  			defaultCPUset: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8),
   324  			assignments: map[string]map[string]cpuset.CPUSet{
   325  				"pod": {
   326  					"c1": cpuset.New(),
   327  				},
   328  			},
   329  		},
   330  	}
   331  
   332  	// create temp dir
   333  	testingDir, err := os.MkdirTemp("", "cpumanager_state_test")
   334  	if err != nil {
   335  		t.Fatal(err)
   336  	}
   337  	defer os.RemoveAll(testingDir)
   338  
   339  	cpm, err := checkpointmanager.NewCheckpointManager(testingDir)
   340  	if err != nil {
   341  		t.Fatalf("could not create testing checkpoint manager: %v", err)
   342  	}
   343  
   344  	for _, tc := range testCases {
   345  		t.Run(tc.description, func(t *testing.T) {
   346  			// ensure there is no previous checkpoint
   347  			cpm.RemoveCheckpoint(testingCheckpoint)
   348  
   349  			state, err := NewCheckpointState(testingDir, testingCheckpoint, "none", nil)
   350  			if err != nil {
   351  				t.Fatalf("could not create testing checkpointState instance: %v", err)
   352  			}
   353  			state.SetDefaultCPUSet(tc.defaultCPUset)
   354  
   355  			for pod := range tc.assignments {
   356  				for container, set := range tc.assignments[pod] {
   357  					state.SetCPUSet(pod, container, set)
   358  					if cpus, _ := state.GetCPUSet(pod, container); !cpus.Equals(set) {
   359  						t.Fatalf("state inconsistent, got %q instead of %q", set, cpus)
   360  					}
   361  
   362  					state.Delete(pod, container)
   363  					if _, ok := state.GetCPUSet(pod, container); ok {
   364  						t.Fatal("deleted container still existing in state")
   365  					}
   366  				}
   367  			}
   368  		})
   369  	}
   370  }
   371  
   372  func TestCheckpointStateClear(t *testing.T) {
   373  	testCases := []struct {
   374  		description   string
   375  		defaultCPUset cpuset.CPUSet
   376  		assignments   map[string]map[string]cpuset.CPUSet
   377  	}{
   378  		{
   379  			"Valid state",
   380  			cpuset.New(1, 5, 10),
   381  			map[string]map[string]cpuset.CPUSet{
   382  				"pod": {
   383  					"container1": cpuset.New(1, 4),
   384  				},
   385  			},
   386  		},
   387  	}
   388  
   389  	for _, tc := range testCases {
   390  		t.Run(tc.description, func(t *testing.T) {
   391  			// create temp dir
   392  			testingDir, err := os.MkdirTemp("", "cpumanager_state_test")
   393  			if err != nil {
   394  				t.Fatal(err)
   395  			}
   396  			defer os.RemoveAll(testingDir)
   397  
   398  			state, err := NewCheckpointState(testingDir, testingCheckpoint, "none", nil)
   399  			if err != nil {
   400  				t.Fatalf("could not create testing checkpointState instance: %v", err)
   401  			}
   402  
   403  			state.SetDefaultCPUSet(tc.defaultCPUset)
   404  			state.SetCPUAssignments(tc.assignments)
   405  
   406  			state.ClearState()
   407  			if !cpuset.New().Equals(state.GetDefaultCPUSet()) {
   408  				t.Fatal("cleared state with non-empty default cpu set")
   409  			}
   410  			for pod := range tc.assignments {
   411  				for container := range tc.assignments[pod] {
   412  					if _, ok := state.GetCPUSet(pod, container); ok {
   413  						t.Fatalf("container %q in pod %q with non-default cpu set in cleared state", container, pod)
   414  					}
   415  				}
   416  			}
   417  		})
   418  	}
   419  }
   420  
   421  func AssertStateEqual(t *testing.T, sf State, sm State) {
   422  	cpusetSf := sf.GetDefaultCPUSet()
   423  	cpusetSm := sm.GetDefaultCPUSet()
   424  	if !cpusetSf.Equals(cpusetSm) {
   425  		t.Errorf("State CPUSet mismatch. Have %v, want %v", cpusetSf, cpusetSm)
   426  	}
   427  
   428  	cpuassignmentSf := sf.GetCPUAssignments()
   429  	cpuassignmentSm := sm.GetCPUAssignments()
   430  	if !reflect.DeepEqual(cpuassignmentSf, cpuassignmentSm) {
   431  		t.Errorf("State CPU assignments mismatch. Have %s, want %s", cpuassignmentSf, cpuassignmentSm)
   432  	}
   433  }
   434  

View as plain text