...

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

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

     1  /*
     2  Copyright 2020 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  	"strings"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  
    26  	v1 "k8s.io/api/core/v1"
    27  	"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
    28  	testutil "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state/testing"
    29  )
    30  
    31  const testingCheckpoint = "memorymanager_checkpoint_test"
    32  
    33  // assertStateEqual marks provided test as failed if provided states differ
    34  func assertStateEqual(t *testing.T, restoredState, expectedState State) {
    35  	expectedMachineState := expectedState.GetMachineState()
    36  	restoredMachineState := restoredState.GetMachineState()
    37  	assert.Equal(t, expectedMachineState, restoredMachineState, "expected MachineState does not equal to restored one")
    38  
    39  	expectedMemoryAssignments := expectedState.GetMemoryAssignments()
    40  	restoredMemoryAssignments := restoredState.GetMemoryAssignments()
    41  	assert.Equal(t, expectedMemoryAssignments, restoredMemoryAssignments, "state memory assignments mismatch")
    42  }
    43  
    44  func TestCheckpointStateRestore(t *testing.T) {
    45  	testCases := []struct {
    46  		description       string
    47  		checkpointContent string
    48  		expectedError     string
    49  		expectedState     *stateMemory
    50  	}{
    51  		{
    52  			"Restore non-existing checkpoint",
    53  			"",
    54  			"",
    55  			&stateMemory{},
    56  		},
    57  		{
    58  			"Restore valid checkpoint",
    59  			`{
    60  				"policyName":"static",
    61  				"machineState":{"0":{"numberOfAssignments":0,"memoryMap":{"memory":{"total":2048,"systemReserved":512,"allocatable":1536,"reserved":512,"free":1024}},"cells":[]}},
    62  				"entries":{"pod":{"container1":[{"numaAffinity":[0],"type":"memory","size":512}]}},
    63  				"checksum": 4215593881
    64  			}`,
    65  			"",
    66  			&stateMemory{
    67  				assignments: ContainerMemoryAssignments{
    68  					"pod": map[string][]Block{
    69  						"container1": {
    70  							{
    71  								NUMAAffinity: []int{0},
    72  								Type:         v1.ResourceMemory,
    73  								Size:         512,
    74  							},
    75  						},
    76  					},
    77  				},
    78  				machineState: NUMANodeMap{
    79  					0: &NUMANodeState{
    80  						MemoryMap: map[v1.ResourceName]*MemoryTable{
    81  							v1.ResourceMemory: {
    82  								Allocatable:    1536,
    83  								Free:           1024,
    84  								Reserved:       512,
    85  								SystemReserved: 512,
    86  								TotalMemSize:   2048,
    87  							},
    88  						},
    89  					},
    90  				},
    91  			},
    92  		},
    93  		{
    94  			"Restore checkpoint with invalid checksum",
    95  			`{
    96  				"policyName":"static",
    97  				"machineState":{"0":{"numberOfAssignments":0,"memoryMap":{"memory":{"total":2048,"systemReserved":512,"allocatable":1536,"reserved":512,"free":1024}},"cells":[]}},
    98  				"entries":{"pod":{"container1":[{"affinity":[0],"type":"memory","size":512}]}},
    99  				"checksum": 101010
   100  			}`,
   101  			"checkpoint is corrupted",
   102  			&stateMemory{},
   103  		},
   104  		{
   105  			"Restore checkpoint with invalid JSON",
   106  			`{`,
   107  			"unexpected end of JSON input",
   108  			&stateMemory{},
   109  		},
   110  	}
   111  
   112  	// create temp dir
   113  	testingDir, err := os.MkdirTemp("", "memorymanager_state_test")
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  	defer os.RemoveAll(testingDir)
   118  
   119  	// create checkpoint manager for testing
   120  	cpm, err := checkpointmanager.NewCheckpointManager(testingDir)
   121  	assert.NoError(t, err, "could not create testing checkpoint manager")
   122  
   123  	for _, tc := range testCases {
   124  		t.Run(tc.description, func(t *testing.T) {
   125  			// ensure there is no previous checkpoint
   126  			assert.NoError(t, cpm.RemoveCheckpoint(testingCheckpoint), "could not remove testing checkpoint")
   127  
   128  			// prepare checkpoint for testing
   129  			if strings.TrimSpace(tc.checkpointContent) != "" {
   130  				checkpoint := &testutil.MockCheckpoint{Content: tc.checkpointContent}
   131  				assert.NoError(t, cpm.CreateCheckpoint(testingCheckpoint, checkpoint), "could not create testing checkpoint")
   132  			}
   133  
   134  			restoredState, err := NewCheckpointState(testingDir, testingCheckpoint, "static")
   135  			if strings.TrimSpace(tc.expectedError) != "" {
   136  				assert.Error(t, err)
   137  				assert.Contains(t, err.Error(), "could not restore state from checkpoint: "+tc.expectedError)
   138  			} else {
   139  				assert.NoError(t, err, "unexpected error while creating checkpointState")
   140  				// compare state after restoration with the one expected
   141  				assertStateEqual(t, restoredState, tc.expectedState)
   142  			}
   143  		})
   144  	}
   145  }
   146  
   147  func TestCheckpointStateStore(t *testing.T) {
   148  	expectedState := &stateMemory{
   149  		assignments: ContainerMemoryAssignments{
   150  			"pod": map[string][]Block{
   151  				"container1": {
   152  					{
   153  						NUMAAffinity: []int{0},
   154  						Type:         v1.ResourceMemory,
   155  						Size:         1024,
   156  					},
   157  				},
   158  			},
   159  		},
   160  		machineState: NUMANodeMap{
   161  			0: &NUMANodeState{
   162  				MemoryMap: map[v1.ResourceName]*MemoryTable{
   163  					v1.ResourceMemory: {
   164  						Allocatable:    1536,
   165  						Free:           512,
   166  						Reserved:       1024,
   167  						SystemReserved: 512,
   168  						TotalMemSize:   2048,
   169  					},
   170  				},
   171  			},
   172  		},
   173  	}
   174  
   175  	// create temp dir
   176  	testingDir, err := os.MkdirTemp("", "memorymanager_state_test")
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  	defer os.RemoveAll(testingDir)
   181  
   182  	cpm, err := checkpointmanager.NewCheckpointManager(testingDir)
   183  	assert.NoError(t, err, "could not create testing checkpoint manager")
   184  
   185  	assert.NoError(t, cpm.RemoveCheckpoint(testingCheckpoint), "could not remove testing checkpoint")
   186  
   187  	cs1, err := NewCheckpointState(testingDir, testingCheckpoint, "static")
   188  	assert.NoError(t, err, "could not create testing checkpointState instance")
   189  
   190  	// set values of cs1 instance so they are stored in checkpoint and can be read by cs2
   191  	cs1.SetMachineState(expectedState.machineState)
   192  	cs1.SetMemoryAssignments(expectedState.assignments)
   193  
   194  	// restore checkpoint with previously stored values
   195  	cs2, err := NewCheckpointState(testingDir, testingCheckpoint, "static")
   196  	assert.NoError(t, err, "could not create testing checkpointState instance")
   197  
   198  	assertStateEqual(t, cs2, expectedState)
   199  }
   200  
   201  func TestCheckpointStateHelpers(t *testing.T) {
   202  	testCases := []struct {
   203  		description  string
   204  		machineState NUMANodeMap
   205  		assignments  ContainerMemoryAssignments
   206  	}{
   207  		{
   208  			description: "One container",
   209  			assignments: ContainerMemoryAssignments{
   210  				"pod": map[string][]Block{
   211  					"container1": {
   212  						{
   213  							NUMAAffinity: []int{0},
   214  							Type:         v1.ResourceMemory,
   215  							Size:         1024,
   216  						},
   217  					},
   218  				},
   219  			},
   220  			machineState: NUMANodeMap{
   221  				0: &NUMANodeState{
   222  					MemoryMap: map[v1.ResourceName]*MemoryTable{
   223  						v1.ResourceMemory: {
   224  							Allocatable:    1536,
   225  							Free:           512,
   226  							Reserved:       1024,
   227  							SystemReserved: 512,
   228  							TotalMemSize:   2048,
   229  						},
   230  					},
   231  					Cells: []int{},
   232  				},
   233  			},
   234  		},
   235  		{
   236  			description: "Two containers",
   237  			assignments: ContainerMemoryAssignments{
   238  				"pod": map[string][]Block{
   239  					"container1": {
   240  						{
   241  							NUMAAffinity: []int{0},
   242  							Type:         v1.ResourceMemory,
   243  							Size:         512,
   244  						},
   245  					},
   246  					"container2": {
   247  						{
   248  							NUMAAffinity: []int{0},
   249  							Type:         v1.ResourceMemory,
   250  							Size:         512,
   251  						},
   252  					},
   253  				},
   254  			},
   255  			machineState: NUMANodeMap{
   256  				0: &NUMANodeState{
   257  					MemoryMap: map[v1.ResourceName]*MemoryTable{
   258  						v1.ResourceMemory: {
   259  							Allocatable:    1536,
   260  							Free:           512,
   261  							Reserved:       1024,
   262  							SystemReserved: 512,
   263  							TotalMemSize:   2048,
   264  						},
   265  					},
   266  					Cells: []int{},
   267  				},
   268  			},
   269  		},
   270  		{
   271  			description: "Container without assigned memory",
   272  			assignments: ContainerMemoryAssignments{
   273  				"pod": map[string][]Block{
   274  					"container1": {},
   275  				},
   276  			},
   277  			machineState: NUMANodeMap{
   278  				0: &NUMANodeState{
   279  					MemoryMap: map[v1.ResourceName]*MemoryTable{
   280  						v1.ResourceMemory: {
   281  							Allocatable:    1536,
   282  							Free:           1536,
   283  							Reserved:       0,
   284  							SystemReserved: 512,
   285  							TotalMemSize:   2048,
   286  						},
   287  					},
   288  					Cells: []int{},
   289  				},
   290  			},
   291  		},
   292  	}
   293  
   294  	// create temp dir
   295  	testingDir, err := os.MkdirTemp("", "memorymanager_state_test")
   296  	if err != nil {
   297  		t.Fatal(err)
   298  	}
   299  	defer os.RemoveAll(testingDir)
   300  
   301  	cpm, err := checkpointmanager.NewCheckpointManager(testingDir)
   302  	assert.NoError(t, err, "could not create testing checkpoint manager")
   303  
   304  	for _, tc := range testCases {
   305  		t.Run(tc.description, func(t *testing.T) {
   306  			// ensure there is no previous checkpoint
   307  			assert.NoError(t, cpm.RemoveCheckpoint(testingCheckpoint), "could not remove testing checkpoint")
   308  
   309  			state, err := NewCheckpointState(testingDir, testingCheckpoint, "static")
   310  			assert.NoError(t, err, "could not create testing checkpoint manager")
   311  
   312  			state.SetMachineState(tc.machineState)
   313  			assert.Equal(t, tc.machineState, state.GetMachineState(), "machine state inconsistent")
   314  
   315  			for pod := range tc.assignments {
   316  				for container, blocks := range tc.assignments[pod] {
   317  					state.SetMemoryBlocks(pod, container, blocks)
   318  					assert.Equal(t, blocks, state.GetMemoryBlocks(pod, container), "memory block inconsistent")
   319  
   320  					state.Delete(pod, container)
   321  					assert.Nil(t, state.GetMemoryBlocks(pod, container), "deleted container still existing in state")
   322  				}
   323  			}
   324  		})
   325  	}
   326  }
   327  
   328  func TestCheckpointStateClear(t *testing.T) {
   329  	testCases := []struct {
   330  		description  string
   331  		machineState NUMANodeMap
   332  		assignments  ContainerMemoryAssignments
   333  	}{
   334  		{
   335  			description: "Valid state cleaning",
   336  			assignments: ContainerMemoryAssignments{
   337  				"pod": map[string][]Block{
   338  					"container1": {
   339  						{
   340  							NUMAAffinity: []int{0},
   341  							Type:         v1.ResourceMemory,
   342  							Size:         1024,
   343  						},
   344  					},
   345  				},
   346  			},
   347  			machineState: NUMANodeMap{
   348  				0: &NUMANodeState{
   349  					MemoryMap: map[v1.ResourceName]*MemoryTable{
   350  						v1.ResourceMemory: {
   351  							Allocatable:    1536,
   352  							Free:           512,
   353  							Reserved:       1024,
   354  							SystemReserved: 512,
   355  							TotalMemSize:   2048,
   356  						},
   357  					},
   358  				},
   359  			},
   360  		},
   361  	}
   362  
   363  	// create temp dir
   364  	testingDir, err := os.MkdirTemp("", "memorymanager_state_test")
   365  	if err != nil {
   366  		t.Fatal(err)
   367  	}
   368  	defer os.RemoveAll(testingDir)
   369  
   370  	for _, tc := range testCases {
   371  		t.Run(tc.description, func(t *testing.T) {
   372  			state, err := NewCheckpointState(testingDir, testingCheckpoint, "static")
   373  			assert.NoError(t, err, "could not create testing checkpoint manager")
   374  
   375  			state.SetMachineState(tc.machineState)
   376  			state.SetMemoryAssignments(tc.assignments)
   377  
   378  			state.ClearState()
   379  			assert.Equal(t, NUMANodeMap{}, state.GetMachineState(), "cleared state with non-empty machine state")
   380  			assert.Equal(t, ContainerMemoryAssignments{}, state.GetMemoryAssignments(), "cleared state with non-empty memory assignments")
   381  		})
   382  	}
   383  }
   384  

View as plain text