github.com/argoproj/argo-cd/v3@v3.2.1/controller/sharding/cache_test.go (about) 1 package sharding 2 3 import ( 4 "testing" 5 6 "github.com/stretchr/testify/assert" 7 8 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 9 dbmocks "github.com/argoproj/argo-cd/v3/util/db/mocks" 10 ) 11 12 func setupTestSharding(shard int, replicas int) *ClusterSharding { 13 shardingAlgorithm := "legacy" // we are using the legacy algorithm as it is deterministic based on the cluster id which is easier to test 14 db := &dbmocks.ArgoDB{} 15 return NewClusterSharding(db, shard, replicas, shardingAlgorithm).(*ClusterSharding) 16 } 17 18 func TestNewClusterSharding(t *testing.T) { 19 shard := 1 20 replicas := 2 21 sharding := setupTestSharding(shard, replicas) 22 23 assert.NotNil(t, sharding) 24 assert.Equal(t, shard, sharding.Shard) 25 assert.Equal(t, replicas, sharding.Replicas) 26 assert.NotNil(t, sharding.Shards) 27 assert.NotNil(t, sharding.Clusters) 28 } 29 30 func TestClusterSharding_Add(t *testing.T) { 31 shard := 1 32 replicas := 2 33 sharding := setupTestSharding(shard, replicas) 34 35 clusterA := &v1alpha1.Cluster{ 36 ID: "2", 37 Server: "https://127.0.0.1:6443", 38 } 39 40 sharding.Add(clusterA) 41 42 clusterB := v1alpha1.Cluster{ 43 ID: "1", 44 Server: "https://kubernetes.default.svc", 45 } 46 47 sharding.Add(&clusterB) 48 49 distribution := sharding.GetDistribution() 50 51 assert.Contains(t, sharding.Clusters, clusterA.Server) 52 assert.Contains(t, sharding.Clusters, clusterB.Server) 53 54 clusterDistribution, ok := distribution[clusterA.Server] 55 assert.True(t, ok) 56 assert.Equal(t, 1, clusterDistribution) 57 58 myClusterDistribution, ok := distribution[clusterB.Server] 59 assert.True(t, ok) 60 assert.Equal(t, 0, myClusterDistribution) 61 62 assert.Len(t, distribution, 2) 63 } 64 65 func TestClusterSharding_AddRoundRobin_Redistributes(t *testing.T) { 66 shard := 1 67 replicas := 2 68 69 db := &dbmocks.ArgoDB{} 70 71 sharding := NewClusterSharding(db, shard, replicas, "round-robin").(*ClusterSharding) 72 73 clusterA := &v1alpha1.Cluster{ 74 ID: "1", 75 Server: "https://127.0.0.1:6443", 76 } 77 sharding.Add(clusterA) 78 79 clusterB := v1alpha1.Cluster{ 80 ID: "3", 81 Server: "https://kubernetes.default.svc", 82 } 83 sharding.Add(&clusterB) 84 85 distributionBefore := sharding.GetDistribution() 86 87 assert.Contains(t, sharding.Clusters, clusterA.Server) 88 assert.Contains(t, sharding.Clusters, clusterB.Server) 89 90 clusterDistributionA, ok := distributionBefore[clusterA.Server] 91 assert.True(t, ok) 92 assert.Equal(t, 0, clusterDistributionA) 93 94 clusterDistributionB, ok := distributionBefore[clusterB.Server] 95 assert.True(t, ok) 96 assert.Equal(t, 1, clusterDistributionB) 97 98 assert.Len(t, distributionBefore, 2) 99 100 clusterC := v1alpha1.Cluster{ 101 ID: "2", 102 Server: "https://1.1.1.1", 103 } 104 sharding.Add(&clusterC) 105 106 distributionAfter := sharding.GetDistribution() 107 108 assert.Contains(t, sharding.Clusters, clusterA.Server) 109 assert.Contains(t, sharding.Clusters, clusterB.Server) 110 assert.Contains(t, sharding.Clusters, clusterC.Server) 111 112 clusterDistributionA, ok = distributionAfter[clusterA.Server] 113 assert.True(t, ok) 114 assert.Equal(t, 0, clusterDistributionA) 115 116 clusterDistributionC, ok := distributionAfter[clusterC.Server] 117 assert.True(t, ok) 118 assert.Equal(t, 1, clusterDistributionC) // will be assigned to shard 1 because the .ID is smaller then the "B" cluster 119 120 clusterDistributionB, ok = distributionAfter[clusterB.Server] 121 assert.True(t, ok) 122 assert.Equal(t, 0, clusterDistributionB) // will be reassigned to shard 0 because the .ID is bigger then the "C" cluster 123 } 124 125 func TestClusterSharding_Delete(t *testing.T) { 126 shard := 1 127 replicas := 2 128 sharding := setupTestSharding(shard, replicas) 129 130 sharding.Init( 131 &v1alpha1.ClusterList{ 132 Items: []v1alpha1.Cluster{ 133 { 134 ID: "2", 135 Server: "https://127.0.0.1:6443", 136 }, 137 { 138 ID: "1", 139 Server: "https://kubernetes.default.svc", 140 }, 141 }, 142 }, 143 &v1alpha1.ApplicationList{ 144 Items: []v1alpha1.Application{ 145 createApp("app2", "https://127.0.0.1:6443"), 146 createApp("app1", "https://kubernetes.default.svc"), 147 }, 148 }, 149 ) 150 151 sharding.Delete("https://kubernetes.default.svc") 152 distribution := sharding.GetDistribution() 153 assert.Len(t, distribution, 1) 154 } 155 156 func TestClusterSharding_Update(t *testing.T) { 157 shard := 1 158 replicas := 2 159 sharding := setupTestSharding(shard, replicas) 160 161 sharding.Init( 162 &v1alpha1.ClusterList{ 163 Items: []v1alpha1.Cluster{ 164 { 165 ID: "2", 166 Server: "https://127.0.0.1:6443", 167 }, 168 { 169 ID: "1", 170 Server: "https://kubernetes.default.svc", 171 }, 172 }, 173 }, 174 &v1alpha1.ApplicationList{ 175 Items: []v1alpha1.Application{ 176 createApp("app2", "https://127.0.0.1:6443"), 177 createApp("app1", "https://kubernetes.default.svc"), 178 }, 179 }, 180 ) 181 182 distributionBefore := sharding.GetDistribution() 183 assert.Len(t, distributionBefore, 2) 184 185 distributionA, ok := distributionBefore["https://kubernetes.default.svc"] 186 assert.True(t, ok) 187 assert.Equal(t, 0, distributionA) 188 189 sharding.Update(&v1alpha1.Cluster{ 190 ID: "1", 191 Server: "https://kubernetes.default.svc", 192 }, &v1alpha1.Cluster{ 193 ID: "4", 194 Server: "https://kubernetes.default.svc", 195 }) 196 197 distributionAfter := sharding.GetDistribution() 198 assert.Len(t, distributionAfter, 2) 199 200 distributionA, ok = distributionAfter["https://kubernetes.default.svc"] 201 assert.True(t, ok) 202 assert.Equal(t, 1, distributionA) 203 } 204 205 func TestClusterSharding_UpdateServerName(t *testing.T) { 206 shard := 1 207 replicas := 2 208 sharding := setupTestSharding(shard, replicas) 209 210 sharding.Init( 211 &v1alpha1.ClusterList{ 212 Items: []v1alpha1.Cluster{ 213 { 214 ID: "2", 215 Server: "https://127.0.0.1:6443", 216 }, 217 { 218 ID: "1", 219 Server: "https://kubernetes.default.svc", 220 }, 221 }, 222 }, 223 &v1alpha1.ApplicationList{ 224 Items: []v1alpha1.Application{ 225 createApp("app2", "https://127.0.0.1:6443"), 226 createApp("app1", "https://kubernetes.default.svc"), 227 }, 228 }, 229 ) 230 231 distributionBefore := sharding.GetDistribution() 232 assert.Len(t, distributionBefore, 2) 233 234 distributionA, ok := distributionBefore["https://kubernetes.default.svc"] 235 assert.True(t, ok) 236 assert.Equal(t, 0, distributionA) 237 238 sharding.Update(&v1alpha1.Cluster{ 239 ID: "1", 240 Server: "https://kubernetes.default.svc", 241 }, &v1alpha1.Cluster{ 242 ID: "1", 243 Server: "https://server2", 244 }) 245 246 distributionAfter := sharding.GetDistribution() 247 assert.Len(t, distributionAfter, 2) 248 249 _, ok = distributionAfter["https://kubernetes.default.svc"] 250 assert.False(t, ok) // the old server name should not be present anymore 251 252 _, ok = distributionAfter["https://server2"] 253 assert.True(t, ok) // the new server name should be present 254 } 255 256 func TestClusterSharding_IsManagedCluster(t *testing.T) { 257 replicas := 2 258 sharding0 := setupTestSharding(0, replicas) 259 260 sharding0.Init( 261 &v1alpha1.ClusterList{ 262 Items: []v1alpha1.Cluster{ 263 { 264 ID: "1", 265 Server: "https://kubernetes.default.svc", 266 }, 267 { 268 ID: "2", 269 Server: "https://127.0.0.1:6443", 270 }, 271 }, 272 }, 273 &v1alpha1.ApplicationList{ 274 Items: []v1alpha1.Application{ 275 createApp("app2", "https://127.0.0.1:6443"), 276 createApp("app1", "https://kubernetes.default.svc"), 277 }, 278 }, 279 ) 280 281 assert.True(t, sharding0.IsManagedCluster(&v1alpha1.Cluster{ 282 ID: "1", 283 Server: "https://kubernetes.default.svc", 284 })) 285 286 assert.False(t, sharding0.IsManagedCluster(&v1alpha1.Cluster{ 287 ID: "2", 288 Server: "https://127.0.0.1:6443", 289 })) 290 291 sharding1 := setupTestSharding(1, replicas) 292 293 sharding1.Init( 294 &v1alpha1.ClusterList{ 295 Items: []v1alpha1.Cluster{ 296 { 297 ID: "2", 298 Server: "https://127.0.0.1:6443", 299 }, 300 { 301 ID: "1", 302 Server: "https://kubernetes.default.svc", 303 }, 304 }, 305 }, 306 &v1alpha1.ApplicationList{ 307 Items: []v1alpha1.Application{ 308 createApp("app2", "https://127.0.0.1:6443"), 309 createApp("app1", "https://kubernetes.default.svc"), 310 }, 311 }, 312 ) 313 314 assert.False(t, sharding1.IsManagedCluster(&v1alpha1.Cluster{ 315 ID: "1", 316 Server: "https://kubernetes.default.svc", 317 })) 318 319 assert.True(t, sharding1.IsManagedCluster(&v1alpha1.Cluster{ 320 ID: "2", 321 Server: "https://127.0.0.1:6443", 322 })) 323 } 324 325 func TestClusterSharding_ClusterShardOfResourceShouldNotBeChanged(t *testing.T) { 326 shard := 1 327 replicas := 2 328 sharding := setupTestSharding(shard, replicas) 329 330 Int64Ptr := func(i int64) *int64 { 331 return &i 332 } 333 334 clusterWithNil := &v1alpha1.Cluster{ 335 ID: "2", 336 Server: "https://127.0.0.1:6443", 337 Shard: nil, 338 } 339 340 clusterWithValue := &v1alpha1.Cluster{ 341 ID: "1", 342 Server: "https://kubernetes.default.svc", 343 Shard: Int64Ptr(1), 344 } 345 346 clusterWithToBigValue := &v1alpha1.Cluster{ 347 ID: "3", 348 Server: "https://1.1.1.1", 349 Shard: Int64Ptr(999), // shard value is explicitly bigger than the number of replicas 350 } 351 352 sharding.Init( 353 &v1alpha1.ClusterList{ 354 Items: []v1alpha1.Cluster{ 355 *clusterWithNil, 356 *clusterWithValue, 357 *clusterWithToBigValue, 358 }, 359 }, 360 &v1alpha1.ApplicationList{ 361 Items: []v1alpha1.Application{ 362 createApp("app2", "https://127.0.0.1:6443"), 363 createApp("app1", "https://kubernetes.default.svc"), 364 }, 365 }, 366 ) 367 distribution := sharding.GetDistribution() 368 assert.Len(t, distribution, 3) 369 370 assert.Nil(t, sharding.Clusters[clusterWithNil.Server].Shard) 371 372 assert.NotNil(t, sharding.Clusters[clusterWithValue.Server].Shard) 373 assert.Equal(t, int64(1), *sharding.Clusters[clusterWithValue.Server].Shard) 374 assert.Equal(t, 1, distribution[clusterWithValue.Server]) 375 376 assert.NotNil(t, sharding.Clusters[clusterWithToBigValue.Server].Shard) 377 assert.Equal(t, int64(999), *sharding.Clusters[clusterWithToBigValue.Server].Shard) 378 assert.Equal(t, 0, distribution[clusterWithToBigValue.Server]) // will be assigned to shard 0 because the value is bigger than the number of replicas 379 } 380 381 func TestHasShardingUpdates(t *testing.T) { 382 Int64Ptr := func(i int64) *int64 { 383 return &i 384 } 385 386 testCases := []struct { 387 name string 388 old *v1alpha1.Cluster 389 new *v1alpha1.Cluster 390 expected bool 391 }{ 392 { 393 name: "No updates", 394 old: &v1alpha1.Cluster{ 395 Server: "https://kubernetes.default.svc", 396 Shard: Int64Ptr(1), 397 }, 398 new: &v1alpha1.Cluster{ 399 Server: "https://kubernetes.default.svc", 400 Shard: Int64Ptr(1), 401 }, 402 expected: false, 403 }, 404 { 405 name: "Updates", 406 old: &v1alpha1.Cluster{ 407 Server: "https://kubernetes.default.svc", 408 Shard: Int64Ptr(1), 409 }, 410 new: &v1alpha1.Cluster{ 411 Server: "https://kubernetes.default.svc", 412 Shard: Int64Ptr(2), 413 }, 414 expected: true, 415 }, 416 { 417 name: "Old is nil", 418 old: nil, 419 new: &v1alpha1.Cluster{ 420 Server: "https://kubernetes.default.svc", 421 Shard: Int64Ptr(2), 422 }, 423 expected: false, 424 }, 425 { 426 name: "New is nil", 427 old: &v1alpha1.Cluster{ 428 Server: "https://kubernetes.default.svc", 429 Shard: Int64Ptr(2), 430 }, 431 new: nil, 432 expected: false, 433 }, 434 { 435 name: "Both are nil", 436 old: nil, 437 new: nil, 438 expected: false, 439 }, 440 { 441 name: "Both shards are nil", 442 old: &v1alpha1.Cluster{ 443 Server: "https://kubernetes.default.svc", 444 Shard: nil, 445 }, 446 new: &v1alpha1.Cluster{ 447 Server: "https://kubernetes.default.svc", 448 Shard: nil, 449 }, 450 expected: false, 451 }, 452 { 453 name: "Old shard is nil", 454 old: &v1alpha1.Cluster{ 455 Server: "https://kubernetes.default.svc", 456 Shard: nil, 457 }, 458 new: &v1alpha1.Cluster{ 459 Server: "https://kubernetes.default.svc", 460 Shard: Int64Ptr(2), 461 }, 462 expected: true, 463 }, 464 { 465 name: "New shard is nil", 466 old: &v1alpha1.Cluster{ 467 Server: "https://kubernetes.default.svc", 468 Shard: Int64Ptr(2), 469 }, 470 new: &v1alpha1.Cluster{ 471 Server: "https://kubernetes.default.svc", 472 Shard: nil, 473 }, 474 expected: true, 475 }, 476 { 477 name: "Cluster ID has changed", 478 old: &v1alpha1.Cluster{ 479 ID: "1", 480 Server: "https://kubernetes.default.svc", 481 Shard: Int64Ptr(2), 482 }, 483 new: &v1alpha1.Cluster{ 484 ID: "2", 485 Server: "https://kubernetes.default.svc", 486 Shard: Int64Ptr(2), 487 }, 488 expected: true, 489 }, 490 { 491 name: "Server has changed", 492 old: &v1alpha1.Cluster{ 493 ID: "1", 494 Server: "https://server1", 495 Shard: Int64Ptr(2), 496 }, 497 new: &v1alpha1.Cluster{ 498 ID: "1", 499 Server: "https://server2", 500 Shard: Int64Ptr(2), 501 }, 502 expected: true, 503 }, 504 } 505 506 for _, tc := range testCases { 507 t.Run(tc.name, func(t *testing.T) { 508 assert.Equal(t, tc.expected, hasShardingUpdates(tc.old, tc.new)) 509 }) 510 } 511 }