github.com/cilium/cilium@v1.16.2/operator/pkg/ciliumidentity/cache_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package ciliumidentity 5 6 import ( 7 "fmt" 8 "os" 9 "reflect" 10 "sync" 11 "testing" 12 13 "github.com/cilium/cilium/pkg/labels" 14 15 "github.com/stretchr/testify/assert" 16 17 cestest "github.com/cilium/cilium/operator/pkg/ciliumendpointslice/testutils" 18 "github.com/cilium/cilium/pkg/identity/key" 19 capi_v2a1 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" 20 "github.com/cilium/cilium/pkg/labelsfilter" 21 ) 22 23 var ( 24 k8sLables_A = map[string]string{"a1": "1", "a2": "2"} 25 k8sLables_B = map[string]string{"b1": "1", "b2": "2"} 26 k8sLables_B_duplicate = map[string]string{"b1": "1", "b2": "2"} 27 k8sLables_C = map[string]string{"c1": "1", "c2": "2"} 28 ) 29 30 func TestMain(m *testing.M) { 31 labelsfilter.ParseLabelPrefixCfg(nil, nil, "") 32 33 os.Exit(m.Run()) 34 } 35 36 func TestCIDState(t *testing.T) { 37 // The subtests below share the same state to serially test insert, lookup and 38 // remove operations of CIDState. 39 state := NewCIDState() 40 k1 := key.GetCIDKeyFromLabels(k8sLables_A, labels.LabelSourceK8s) 41 k2 := key.GetCIDKeyFromLabels(k8sLables_B, labels.LabelSourceK8s) 42 k3 := key.GetCIDKeyFromLabels(k8sLables_B_duplicate, labels.LabelSourceK8s) 43 44 t.Run("Insert into CID state", func(t *testing.T) { 45 state.Upsert("1", k1) 46 expectedState := &CIDState{ 47 idToLabels: map[string]*key.GlobalIdentity{"1": k1}, 48 labelsToID: map[string]*SecIDs{ 49 k1.GetKey(): { 50 selectedID: "1", 51 ids: map[string]struct{}{"1": {}}, 52 }, 53 }, 54 } 55 56 assert.NoError(t, validateCIDState(state, expectedState), "cid 1 added") 57 58 state.Upsert("2", k2) 59 expectedState = &CIDState{ 60 idToLabels: map[string]*key.GlobalIdentity{"1": k1, "2": k2}, 61 labelsToID: map[string]*SecIDs{ 62 k1.GetKey(): { 63 selectedID: "1", 64 ids: map[string]struct{}{"1": {}}, 65 }, 66 k2.GetKey(): { 67 selectedID: "2", 68 ids: map[string]struct{}{"2": {}}, 69 }, 70 }, 71 } 72 73 assert.NoError(t, validateCIDState(state, expectedState), "cid 2 added") 74 75 state.Upsert("3", k3) 76 expectedState = &CIDState{ 77 idToLabels: map[string]*key.GlobalIdentity{"1": k1, "2": k2, "3": k3}, 78 labelsToID: map[string]*SecIDs{ 79 k1.GetKey(): { 80 selectedID: "1", 81 ids: map[string]struct{}{"1": {}}, 82 }, 83 k2.GetKey(): { 84 selectedID: "2", 85 ids: map[string]struct{}{"2": {}, "3": {}}, 86 }, 87 }, 88 } 89 90 assert.NoError(t, validateCIDState(state, expectedState), "cid 3 added - duplicate") 91 }) 92 93 t.Run("Lookup CID state", func(t *testing.T) { 94 _, exists := state.LookupByID("0") 95 assert.Equal(t, false, exists, "cid 0 LookupByID - not found") 96 97 cidKey, exists := state.LookupByID("1") 98 assert.Equal(t, true, exists, "cid 1 LookupByID - found") 99 assert.Equal(t, key.GetCIDKeyFromLabels(k8sLables_A, labels.LabelSourceK8s), cidKey, "cid 1 LookupByID - correct key") 100 101 _, exists = state.LookupByKey(key.GetCIDKeyFromLabels(k8sLables_C, labels.LabelSourceK8s)) 102 assert.Equal(t, false, exists, "labels C LookupByKey - not found") 103 104 cidName, exists := state.LookupByKey(key.GetCIDKeyFromLabels(k8sLables_A, labels.LabelSourceK8s)) 105 assert.Equal(t, true, exists, "labels C LookupByKey - not found") 106 assert.Equal(t, "1", cidName, "labels C LookupByKey - correct CID") 107 }) 108 109 t.Run("Remove from CID state", func(t *testing.T) { 110 state.Remove("2") 111 expectedState := &CIDState{ 112 idToLabels: map[string]*key.GlobalIdentity{"1": k1, "3": k3}, 113 labelsToID: map[string]*SecIDs{ 114 k1.GetKey(): { 115 selectedID: "1", 116 ids: map[string]struct{}{"1": {}}, 117 }, 118 k2.GetKey(): { 119 selectedID: "3", 120 ids: map[string]struct{}{"3": {}}, 121 }, 122 }, 123 } 124 125 assert.NoError(t, validateCIDState(state, expectedState), "cid 2 removed") 126 127 _, exists := state.LookupByID("2") 128 assert.Equal(t, false, exists, "cid 2 LookupByID - not found") 129 130 state.Remove("3") 131 expectedState = &CIDState{ 132 idToLabels: map[string]*key.GlobalIdentity{"1": k1}, 133 labelsToID: map[string]*SecIDs{ 134 k1.GetKey(): { 135 selectedID: "1", 136 ids: map[string]struct{}{"1": {}}, 137 }, 138 }, 139 } 140 assert.NoError(t, validateCIDState(state, expectedState), "cid 3 removed") 141 }) 142 } 143 144 func TestCIDStateThreadSafety(t *testing.T) { 145 // This test ensures that no changes to the CID state break its thread safety. 146 // Multiple go routines in parallel continuously keep using CIDState. 147 state := NewCIDState() 148 149 k := key.GetCIDKeyFromLabels(k8sLables_A, labels.LabelSourceK8s) 150 k2 := key.GetCIDKeyFromLabels(k8sLables_B, labels.LabelSourceK8s) 151 152 wg := sync.WaitGroup{} 153 queryStateFunc := func() { 154 for i := 0; i < 500; i++ { 155 state.LookupByID("1000") 156 state.Upsert("1000", k) 157 state.LookupByKey(k) 158 state.Upsert("2000", k2) 159 state.Remove("1000") 160 state.LookupByID("2000") 161 } 162 163 wg.Done() 164 } 165 166 for i := 0; i < 5; i++ { 167 wg.Add(1) 168 go queryStateFunc() 169 } 170 171 wg.Wait() 172 } 173 174 func validateCIDState(state, expectedState *CIDState) error { 175 if !reflect.DeepEqual(state.idToLabels, expectedState.idToLabels) { 176 return fmt.Errorf("failed to validate the state, expected idToLabels %v, got %v", expectedState.idToLabels, state.idToLabels) 177 } 178 179 if !reflect.DeepEqual(state.labelsToID, expectedState.labelsToID) { 180 return fmt.Errorf("failed to validate the state, expected labelsToID %v, got %v", expectedState.labelsToID, state.labelsToID) 181 } 182 183 return nil 184 } 185 186 func TestCIDUsageInPods(t *testing.T) { 187 state := NewCIDUsageInPods() 188 189 assertTxt := "Empty state" 190 cidName1 := "1000" 191 podName1 := "pod1" 192 assert.Equal(t, 0, state.CIDUsageCount(cidName1), assertTxt) 193 194 usedCID, exists := state.CIDUsedByPod(podName1) 195 assert.Equal(t, false, exists, assertTxt) 196 assert.Equal(t, "", usedCID, assertTxt) 197 198 prevCID, count, err := state.RemovePod(podName1) 199 assert.Error(t, err) 200 assert.Equal(t, "", prevCID, assertTxt) 201 assert.Equal(t, 0, count, assertTxt) 202 203 assertTxt = "Assign CID to Pod 1" 204 prevCID, count = state.AssignCIDToPod(podName1, cidName1) 205 assert.Equal(t, "", prevCID, assertTxt) 206 assert.Equal(t, 0, count, assertTxt) 207 assert.Equal(t, 1, state.CIDUsageCount(cidName1), assertTxt) 208 209 usedCID, exists = state.CIDUsedByPod(podName1) 210 assert.Equal(t, true, exists, assertTxt) 211 assert.Equal(t, cidName1, usedCID, assertTxt) 212 213 assertTxt = "Assign CID to Pod 2" 214 podName2 := "pod2" 215 prevCID, count = state.AssignCIDToPod(podName2, cidName1) 216 assert.Equal(t, "", prevCID, assertTxt) 217 assert.Equal(t, 0, count, assertTxt) 218 assert.Equal(t, 2, state.CIDUsageCount(cidName1), assertTxt) 219 220 usedCID, exists = state.CIDUsedByPod(podName2) 221 assert.Equal(t, true, exists, assertTxt) 222 assert.Equal(t, cidName1, usedCID, assertTxt) 223 224 assertTxt = "Assign CID 2 to Pod 2" 225 cidName2 := "2000" 226 prevCID, count = state.AssignCIDToPod(podName2, cidName2) 227 assert.Equal(t, cidName1, prevCID, assertTxt) 228 assert.Equal(t, 1, count, assertTxt) 229 assert.Equal(t, 1, state.CIDUsageCount(cidName2), assertTxt) 230 231 usedCID, exists = state.CIDUsedByPod(podName2) 232 assert.Equal(t, true, exists, assertTxt) 233 assert.Equal(t, cidName2, usedCID, assertTxt) 234 235 assertTxt = "Assign CID 2 to Pod 1" 236 prevCID, count = state.AssignCIDToPod(podName1, cidName2) 237 assert.Equal(t, cidName1, prevCID, assertTxt) 238 assert.Equal(t, 0, count, assertTxt) 239 assert.Equal(t, 2, state.CIDUsageCount(cidName2), assertTxt) 240 assert.Equal(t, 0, state.CIDUsageCount(cidName1), assertTxt) 241 242 usedCID, exists = state.CIDUsedByPod(podName1) 243 assert.Equal(t, true, exists, assertTxt) 244 assert.Equal(t, cidName2, usedCID, assertTxt) 245 246 assertTxt = "Again assign CID 2 to Pod 1" 247 prevCID, count = state.AssignCIDToPod(podName1, cidName2) 248 assert.Equal(t, cidName2, prevCID, assertTxt) 249 assert.Equal(t, 2, count, assertTxt) 250 assert.Equal(t, 2, state.CIDUsageCount(cidName2), assertTxt) 251 assert.Equal(t, 0, state.CIDUsageCount(cidName1), assertTxt) 252 253 assertTxt = "Remove Pod 1" 254 prevCID, count, err = state.RemovePod(podName1) 255 assert.NoError(t, err) 256 assert.Equal(t, cidName2, prevCID, assertTxt) 257 assert.Equal(t, 1, count, assertTxt) 258 assert.Equal(t, 1, state.CIDUsageCount(cidName2), assertTxt) 259 260 usedCID, exists = state.CIDUsedByPod(podName1) 261 assert.Equal(t, false, exists, assertTxt) 262 assert.Equal(t, "", usedCID, assertTxt) 263 264 assertTxt = "Remove Pod 2" 265 prevCID, count, err = state.RemovePod(podName2) 266 assert.NoError(t, err) 267 assert.Equal(t, cidName2, prevCID, assertTxt) 268 assert.Equal(t, 0, count, assertTxt) 269 assert.Equal(t, 0, state.CIDUsageCount(cidName2), assertTxt) 270 271 usedCID, exists = state.CIDUsedByPod(podName2) 272 assert.Equal(t, false, exists, assertTxt) 273 assert.Equal(t, "", usedCID, assertTxt) 274 } 275 276 func TestCIDUsageInCES(t *testing.T) { 277 cep1 := cestest.CreateManagerEndpoint("cep1", 1000) 278 cep2 := cestest.CreateManagerEndpoint("cep2", 1000) 279 cep3 := cestest.CreateManagerEndpoint("cep3", 2000) 280 cep4 := cestest.CreateManagerEndpoint("cep4", 3000) 281 ces1 := cestest.CreateStoreEndpointSlice("ces1", "ns", []capi_v2a1.CoreCiliumEndpoint{cep1, cep2, cep3, cep4}) 282 283 cep5 := cestest.CreateManagerEndpoint("cep5", 1000) 284 cep6 := cestest.CreateManagerEndpoint("cep6", 1000) 285 cep7 := cestest.CreateManagerEndpoint("cep7", 2000) 286 ces2 := cestest.CreateStoreEndpointSlice("ces2", "ns", []capi_v2a1.CoreCiliumEndpoint{cep5, cep6, cep7}) 287 288 assertTxt := "CES 1 is added" 289 state := NewCIDUsageInCES() 290 unusedCIDs := state.ProcessCESUpsert(ces1.Name, ces1.Endpoints) 291 assert.Equal(t, 0, len(unusedCIDs), assertTxt) 292 assert.Equal(t, 2, state.CIDUsageCount("1000"), assertTxt) 293 assert.Equal(t, 1, state.CIDUsageCount("2000"), assertTxt) 294 assert.Equal(t, 1, state.CIDUsageCount("3000"), assertTxt) 295 296 assertTxt = "CES 2 is added" 297 unusedCIDs = state.ProcessCESUpsert(ces2.Name, ces2.Endpoints) 298 assert.Equal(t, 0, len(unusedCIDs), assertTxt) 299 assert.Equal(t, 4, state.CIDUsageCount("1000"), assertTxt) 300 assert.Equal(t, 2, state.CIDUsageCount("2000"), assertTxt) 301 assert.Equal(t, 1, state.CIDUsageCount("3000"), assertTxt) 302 303 assertTxt = "Endpoint with CID 3000 is removed from CES 1" 304 ces1 = cestest.CreateStoreEndpointSlice("ces1", "ns", []capi_v2a1.CoreCiliumEndpoint{cep1, cep2, cep3}) 305 unusedCIDs = state.ProcessCESUpsert(ces1.Name, ces1.Endpoints) 306 assert.Equal(t, 1, len(unusedCIDs), assertTxt) 307 if len(unusedCIDs) > 0 { 308 assert.Equal(t, int64(3000), unusedCIDs[0], assertTxt) 309 } 310 assert.Equal(t, 4, state.CIDUsageCount("1000"), assertTxt) 311 assert.Equal(t, 2, state.CIDUsageCount("2000"), assertTxt) 312 assert.Equal(t, 0, state.CIDUsageCount("3000"), assertTxt) 313 314 assertTxt = "CES 1 is removed" 315 unusedCIDs = state.ProcessCESDelete(ces1.Name, ces1.Endpoints) 316 assert.Equal(t, 0, len(unusedCIDs), assertTxt) 317 assert.Equal(t, 2, state.CIDUsageCount("1000"), assertTxt) 318 assert.Equal(t, 1, state.CIDUsageCount("2000"), assertTxt) 319 320 assertTxt = "CES 2 is removed" 321 unusedCIDs = state.ProcessCESDelete(ces1.Name, ces1.Endpoints) 322 assert.Equal(t, 2, len(unusedCIDs), assertTxt) 323 assert.Equal(t, 0, state.CIDUsageCount("1000"), assertTxt) 324 assert.Equal(t, 0, state.CIDUsageCount("2000"), assertTxt) 325 }