github.com/argoproj/argo-cd/v2@v2.10.9/server/cluster/cluster_test.go (about) 1 package cluster 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "reflect" 8 "testing" 9 "time" 10 11 "github.com/argoproj/argo-cd/v2/common" 12 "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster" 13 clusterapi "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster" 14 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 15 appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 16 servercache "github.com/argoproj/argo-cd/v2/server/cache" 17 "github.com/argoproj/argo-cd/v2/test" 18 cacheutil "github.com/argoproj/argo-cd/v2/util/cache" 19 appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" 20 "github.com/argoproj/argo-cd/v2/util/db" 21 dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" 22 "github.com/argoproj/argo-cd/v2/util/rbac" 23 "github.com/argoproj/argo-cd/v2/util/settings" 24 "github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest" 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/mock" 27 "github.com/stretchr/testify/require" 28 corev1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/client-go/kubernetes/fake" 33 "k8s.io/utils/pointer" 34 ) 35 36 func newServerInMemoryCache() *servercache.Cache { 37 return servercache.NewCache( 38 appstatecache.NewCache( 39 cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Hour)), 40 1*time.Minute, 41 ), 42 1*time.Minute, 43 1*time.Minute, 44 1*time.Minute, 45 ) 46 } 47 48 func newNoopEnforcer() *rbac.Enforcer { 49 enf := rbac.NewEnforcer(fake.NewSimpleClientset(test.NewFakeConfigMap()), test.FakeArgoCDNamespace, common.ArgoCDConfigMapName, nil) 50 enf.EnableEnforce(false) 51 return enf 52 } 53 54 func TestUpdateCluster_RejectInvalidParams(t *testing.T) { 55 testCases := []struct { 56 name string 57 request clusterapi.ClusterUpdateRequest 58 }{ 59 { 60 name: "allowed cluster URL in body, disallowed cluster URL in query", 61 request: clusterapi.ClusterUpdateRequest{Cluster: &v1alpha1.Cluster{Name: "", Server: "https://127.0.0.1", Project: "", ClusterResources: true}, Id: &clusterapi.ClusterID{Type: "", Value: "https://127.0.0.2"}, UpdatedFields: []string{"clusterResources", "project"}}, 62 }, 63 { 64 name: "allowed cluster URL in body, disallowed cluster name in query", 65 request: clusterapi.ClusterUpdateRequest{Cluster: &v1alpha1.Cluster{Name: "", Server: "https://127.0.0.1", Project: "", ClusterResources: true}, Id: &clusterapi.ClusterID{Type: "name", Value: "disallowed-unscoped"}, UpdatedFields: []string{"clusterResources", "project"}}, 66 }, 67 { 68 name: "allowed cluster URL in body, disallowed cluster name in query, changing unscoped to scoped", 69 request: clusterapi.ClusterUpdateRequest{Cluster: &v1alpha1.Cluster{Name: "", Server: "https://127.0.0.1", Project: "allowed-project", ClusterResources: true}, Id: &clusterapi.ClusterID{Type: "", Value: "https://127.0.0.2"}, UpdatedFields: []string{"clusterResources", "project"}}, 70 }, 71 { 72 name: "allowed cluster URL in body, disallowed cluster URL in query, changing unscoped to scoped", 73 request: clusterapi.ClusterUpdateRequest{Cluster: &v1alpha1.Cluster{Name: "", Server: "https://127.0.0.1", Project: "allowed-project", ClusterResources: true}, Id: &clusterapi.ClusterID{Type: "name", Value: "disallowed-unscoped"}, UpdatedFields: []string{"clusterResources", "project"}}, 74 }, 75 } 76 77 db := &dbmocks.ArgoDB{} 78 79 clusters := []v1alpha1.Cluster{ 80 { 81 Name: "allowed-unscoped", 82 Server: "https://127.0.0.1", 83 }, 84 { 85 Name: "disallowed-unscoped", 86 Server: "https://127.0.0.2", 87 }, 88 { 89 Name: "allowed-scoped", 90 Server: "https://127.0.0.3", 91 Project: "allowed-project", 92 }, 93 { 94 Name: "disallowed-scoped", 95 Server: "https://127.0.0.4", 96 Project: "disallowed-project", 97 }, 98 } 99 100 db.On("ListClusters", mock.Anything).Return( 101 func(ctx context.Context) *v1alpha1.ClusterList { 102 return &v1alpha1.ClusterList{ 103 ListMeta: v1.ListMeta{}, 104 Items: clusters, 105 } 106 }, 107 func(ctx context.Context) error { 108 return nil 109 }, 110 ) 111 db.On("UpdateCluster", mock.Anything, mock.Anything).Return( 112 func(ctx context.Context, c *v1alpha1.Cluster) *v1alpha1.Cluster { 113 for _, cluster := range clusters { 114 if c.Server == cluster.Server { 115 return c 116 } 117 } 118 return nil 119 }, 120 func(ctx context.Context, c *v1alpha1.Cluster) error { 121 for _, cluster := range clusters { 122 if c.Server == cluster.Server { 123 return nil 124 } 125 } 126 return fmt.Errorf("cluster '%s' not found", c.Server) 127 }, 128 ) 129 db.On("GetCluster", mock.Anything, mock.Anything).Return( 130 func(ctx context.Context, server string) *v1alpha1.Cluster { 131 for _, cluster := range clusters { 132 if server == cluster.Server { 133 return &cluster 134 } 135 } 136 return nil 137 }, 138 func(ctx context.Context, server string) error { 139 for _, cluster := range clusters { 140 if server == cluster.Server { 141 return nil 142 } 143 } 144 return fmt.Errorf("cluster '%s' not found", server) 145 }, 146 ) 147 148 enf := rbac.NewEnforcer(fake.NewSimpleClientset(test.NewFakeConfigMap()), test.FakeArgoCDNamespace, common.ArgoCDConfigMapName, nil) 149 _ = enf.SetBuiltinPolicy(`p, role:test, clusters, *, https://127.0.0.1, allow 150 p, role:test, clusters, *, allowed-project/*, allow`) 151 enf.SetDefaultRole("role:test") 152 server := NewServer(db, enf, newServerInMemoryCache(), &kubetest.MockKubectlCmd{}) 153 154 for _, c := range testCases { 155 cc := c 156 t.Run(cc.name, func(t *testing.T) { 157 t.Parallel() 158 out, err := server.Update(context.Background(), &cc.request) 159 require.Nil(t, out) 160 assert.ErrorIs(t, err, common.PermissionDeniedAPIError) 161 }) 162 } 163 } 164 165 func TestGetCluster_UrlEncodedName(t *testing.T) { 166 db := &dbmocks.ArgoDB{} 167 168 mockCluster := v1alpha1.Cluster{ 169 Name: "test/ing", 170 Server: "https://127.0.0.1", 171 Namespaces: []string{"default", "kube-system"}, 172 } 173 mockClusterList := v1alpha1.ClusterList{ 174 ListMeta: v1.ListMeta{}, 175 Items: []v1alpha1.Cluster{ 176 mockCluster, 177 }, 178 } 179 180 db.On("ListClusters", mock.Anything).Return(&mockClusterList, nil) 181 182 server := NewServer(db, newNoopEnforcer(), newServerInMemoryCache(), &kubetest.MockKubectlCmd{}) 183 184 cluster, err := server.Get(context.Background(), &clusterapi.ClusterQuery{ 185 Id: &clusterapi.ClusterID{ 186 Type: "name_escaped", 187 Value: "test%2fing", 188 }, 189 }) 190 require.NoError(t, err) 191 192 assert.Equal(t, cluster.Name, "test/ing") 193 } 194 195 func TestGetCluster_NameWithUrlEncodingButShouldNotBeUnescaped(t *testing.T) { 196 db := &dbmocks.ArgoDB{} 197 198 mockCluster := v1alpha1.Cluster{ 199 Name: "test%2fing", 200 Server: "https://127.0.0.1", 201 Namespaces: []string{"default", "kube-system"}, 202 } 203 mockClusterList := v1alpha1.ClusterList{ 204 ListMeta: v1.ListMeta{}, 205 Items: []v1alpha1.Cluster{ 206 mockCluster, 207 }, 208 } 209 210 db.On("ListClusters", mock.Anything).Return(&mockClusterList, nil) 211 212 server := NewServer(db, newNoopEnforcer(), newServerInMemoryCache(), &kubetest.MockKubectlCmd{}) 213 214 cluster, err := server.Get(context.Background(), &clusterapi.ClusterQuery{ 215 Id: &clusterapi.ClusterID{ 216 Type: "name", 217 Value: "test%2fing", 218 }, 219 }) 220 require.NoError(t, err) 221 222 assert.Equal(t, cluster.Name, "test%2fing") 223 } 224 225 func TestUpdateCluster_NoFieldsPaths(t *testing.T) { 226 db := &dbmocks.ArgoDB{} 227 var updated *v1alpha1.Cluster 228 229 clusters := []v1alpha1.Cluster{ 230 { 231 Name: "minikube", 232 Server: "https://127.0.0.1", 233 Namespaces: []string{"default", "kube-system"}, 234 }, 235 } 236 237 clusterList := v1alpha1.ClusterList{ 238 ListMeta: v1.ListMeta{}, 239 Items: clusters, 240 } 241 242 db.On("ListClusters", mock.Anything).Return(&clusterList, nil) 243 db.On("UpdateCluster", mock.Anything, mock.MatchedBy(func(c *v1alpha1.Cluster) bool { 244 updated = c 245 return true 246 })).Return(&v1alpha1.Cluster{}, nil) 247 248 server := NewServer(db, newNoopEnforcer(), newServerInMemoryCache(), &kubetest.MockKubectlCmd{}) 249 250 _, err := server.Update(context.Background(), &clusterapi.ClusterUpdateRequest{ 251 Cluster: &v1alpha1.Cluster{ 252 Name: "minikube", 253 Namespaces: []string{"default", "kube-system"}, 254 }, 255 }) 256 257 require.NoError(t, err) 258 259 assert.Equal(t, updated.Name, "minikube") 260 assert.Equal(t, updated.Namespaces, []string{"default", "kube-system"}) 261 } 262 263 func TestUpdateCluster_FieldsPathSet(t *testing.T) { 264 db := &dbmocks.ArgoDB{} 265 var updated *v1alpha1.Cluster 266 db.On("GetCluster", mock.Anything, "https://127.0.0.1").Return(&v1alpha1.Cluster{ 267 Name: "minikube", 268 Server: "https://127.0.0.1", 269 Namespaces: []string{"default", "kube-system"}, 270 }, nil) 271 db.On("UpdateCluster", mock.Anything, mock.MatchedBy(func(c *v1alpha1.Cluster) bool { 272 updated = c 273 return true 274 })).Return(&v1alpha1.Cluster{}, nil) 275 276 server := NewServer(db, newNoopEnforcer(), newServerInMemoryCache(), &kubetest.MockKubectlCmd{}) 277 278 _, err := server.Update(context.Background(), &clusterapi.ClusterUpdateRequest{ 279 Cluster: &v1alpha1.Cluster{ 280 Server: "https://127.0.0.1", 281 Shard: pointer.Int64(1), 282 }, 283 UpdatedFields: []string{"shard"}, 284 }) 285 286 require.NoError(t, err) 287 288 assert.Equal(t, updated.Name, "minikube") 289 assert.Equal(t, updated.Namespaces, []string{"default", "kube-system"}) 290 assert.Equal(t, *updated.Shard, int64(1)) 291 292 labelEnv := map[string]string{ 293 "env": "qa", 294 } 295 _, err = server.Update(context.Background(), &clusterapi.ClusterUpdateRequest{ 296 Cluster: &v1alpha1.Cluster{ 297 Server: "https://127.0.0.1", 298 Labels: labelEnv, 299 }, 300 UpdatedFields: []string{"labels"}, 301 }) 302 303 require.NoError(t, err) 304 305 assert.Equal(t, updated.Name, "minikube") 306 assert.Equal(t, updated.Namespaces, []string{"default", "kube-system"}) 307 assert.Equal(t, updated.Labels, labelEnv) 308 309 annotationEnv := map[string]string{ 310 "env": "qa", 311 } 312 _, err = server.Update(context.Background(), &clusterapi.ClusterUpdateRequest{ 313 Cluster: &v1alpha1.Cluster{ 314 Server: "https://127.0.0.1", 315 Annotations: annotationEnv, 316 }, 317 UpdatedFields: []string{"annotations"}, 318 }) 319 320 require.NoError(t, err) 321 322 assert.Equal(t, updated.Name, "minikube") 323 assert.Equal(t, updated.Namespaces, []string{"default", "kube-system"}) 324 assert.Equal(t, updated.Annotations, annotationEnv) 325 326 _, err = server.Update(context.Background(), &clusterapi.ClusterUpdateRequest{ 327 Cluster: &v1alpha1.Cluster{ 328 Server: "https://127.0.0.1", 329 Project: "new-project", 330 }, 331 UpdatedFields: []string{"project"}, 332 }) 333 334 require.NoError(t, err) 335 336 assert.Equal(t, updated.Name, "minikube") 337 assert.Equal(t, updated.Namespaces, []string{"default", "kube-system"}) 338 assert.Equal(t, updated.Project, "new-project") 339 } 340 341 func TestDeleteClusterByName(t *testing.T) { 342 testNamespace := "default" 343 clientset := getClientset(nil, testNamespace, &corev1.Secret{ 344 ObjectMeta: metav1.ObjectMeta{ 345 Name: "my-cluster-secret", 346 Namespace: testNamespace, 347 Labels: map[string]string{ 348 common.LabelKeySecretType: common.LabelValueSecretTypeCluster, 349 }, 350 Annotations: map[string]string{ 351 common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD, 352 }, 353 }, 354 Data: map[string][]byte{ 355 "name": []byte("my-cluster-name"), 356 "server": []byte("https://my-cluster-server"), 357 "config": []byte("{}"), 358 }, 359 }) 360 db := db.NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset) 361 server := NewServer(db, newNoopEnforcer(), newServerInMemoryCache(), &kubetest.MockKubectlCmd{}) 362 363 t.Run("Delete Fails When Deleting by Unknown Name", func(t *testing.T) { 364 _, err := server.Delete(context.Background(), &clusterapi.ClusterQuery{ 365 Name: "foo", 366 }) 367 368 assert.EqualError(t, err, `rpc error: code = PermissionDenied desc = permission denied`) 369 }) 370 371 t.Run("Delete Succeeds When Deleting by Name", func(t *testing.T) { 372 _, err := server.Delete(context.Background(), &clusterapi.ClusterQuery{ 373 Name: "my-cluster-name", 374 }) 375 assert.Nil(t, err) 376 377 _, err = db.GetCluster(context.Background(), "https://my-cluster-server") 378 assert.EqualError(t, err, `rpc error: code = NotFound desc = cluster "https://my-cluster-server" not found`) 379 }) 380 } 381 382 func TestRotateAuth(t *testing.T) { 383 testNamespace := "kube-system" 384 token := "eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhcmdvY2QtbWFuYWdlci10b2tlbi10ajc5ciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhcmdvY2QtbWFuYWdlciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjkxZGQzN2NmLThkOTItMTFlOS1hMDkxLWQ2NWYyYWU3ZmE4ZCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTphcmdvY2QtbWFuYWdlciJ9.ytZjt2pDV8-A7DBMR06zQ3wt9cuVEfq262TQw7sdra-KRpDpMPnziMhc8bkwvgW-LGhTWUh5iu1y-1QhEx6mtbCt7vQArlBRxfvM5ys6ClFkplzq5c2TtZ7EzGSD0Up7tdxuG9dvR6TGXYdfFcG779yCdZo2H48sz5OSJfdEriduMEY1iL5suZd3ebOoVi1fGflmqFEkZX6SvxkoArl5mtNP6TvZ1eTcn64xh4ws152hxio42E-eSnl_CET4tpB5vgP5BVlSKW2xB7w2GJxqdETA5LJRI_OilY77dTOp8cMr_Ck3EOeda3zHfh4Okflg8rZFEeAuJYahQNeAILLkcA" 385 config := v1alpha1.ClusterConfig{ 386 BearerToken: token, 387 } 388 389 configMarshal, err := json.Marshal(config) 390 if err != nil { 391 t.Errorf("failed to marshal config for test: %v", err) 392 } 393 394 clientset := getClientset(nil, testNamespace, 395 &corev1.Secret{ 396 ObjectMeta: metav1.ObjectMeta{ 397 Name: "my-cluster-secret", 398 Namespace: testNamespace, 399 Labels: map[string]string{ 400 common.LabelKeySecretType: common.LabelValueSecretTypeCluster, 401 }, 402 Annotations: map[string]string{ 403 common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD, 404 }, 405 }, 406 Data: map[string][]byte{ 407 "name": []byte("my-cluster-name"), 408 "server": []byte("https://my-cluster-name"), 409 "config": configMarshal, 410 }, 411 }, 412 &corev1.Namespace{ 413 ObjectMeta: metav1.ObjectMeta{ 414 Name: "kube-system", 415 }, 416 }, 417 &corev1.Secret{ 418 ObjectMeta: metav1.ObjectMeta{ 419 Name: "argocd-manager-token-tj79r", 420 Namespace: "kube-system", 421 }, 422 Data: map[string][]byte{ 423 "token": []byte(token), 424 }, 425 }, 426 &corev1.ServiceAccount{ 427 ObjectMeta: metav1.ObjectMeta{ 428 Name: "argocd-manager", 429 Namespace: "kube-system", 430 }, 431 Secrets: []corev1.ObjectReference{ 432 { 433 Kind: "Secret", 434 Name: "argocd-manager-token-tj79r", 435 }, 436 }, 437 }) 438 439 db := db.NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset) 440 server := NewServer(db, newNoopEnforcer(), newServerInMemoryCache(), &kubetest.MockKubectlCmd{}) 441 442 t.Run("RotateAuth by Unknown Name", func(t *testing.T) { 443 _, err := server.RotateAuth(context.Background(), &clusterapi.ClusterQuery{ 444 Name: "foo", 445 }) 446 447 assert.EqualError(t, err, `rpc error: code = PermissionDenied desc = permission denied`) 448 }) 449 450 // While the tests results for the next two tests result in an error, they do 451 // demonstrate the proper mapping of cluster names/server to server info (i.e. my-cluster-name 452 // results in https://my-cluster-name info being used and https://my-cluster-name results in https://my-cluster-name). 453 t.Run("RotateAuth by Name - Error from no such host", func(t *testing.T) { 454 _, err := server.RotateAuth(context.Background(), &clusterapi.ClusterQuery{ 455 Name: "my-cluster-name", 456 }) 457 458 require.NotNil(t, err) 459 assert.Contains(t, err.Error(), "Get \"https://my-cluster-name/") 460 }) 461 462 t.Run("RotateAuth by Server - Error from no such host", func(t *testing.T) { 463 _, err := server.RotateAuth(context.Background(), &clusterapi.ClusterQuery{ 464 Server: "https://my-cluster-name", 465 }) 466 467 require.NotNil(t, err) 468 assert.Contains(t, err.Error(), "Get \"https://my-cluster-name/") 469 }) 470 } 471 472 func getClientset(config map[string]string, ns string, objects ...runtime.Object) *fake.Clientset { 473 secret := corev1.Secret{ 474 ObjectMeta: metav1.ObjectMeta{ 475 Name: "argocd-secret", 476 Namespace: ns, 477 }, 478 Data: map[string][]byte{ 479 "admin.password": []byte("test"), 480 "server.secretkey": []byte("test"), 481 }, 482 } 483 cm := corev1.ConfigMap{ 484 ObjectMeta: metav1.ObjectMeta{ 485 Name: "argocd-cm", 486 Namespace: ns, 487 Labels: map[string]string{ 488 "app.kubernetes.io/part-of": "argocd", 489 }, 490 }, 491 Data: config, 492 } 493 return fake.NewSimpleClientset(append(objects, &cm, &secret)...) 494 } 495 496 func TestListCluster(t *testing.T) { 497 db := &dbmocks.ArgoDB{} 498 499 fooCluster := v1alpha1.Cluster{ 500 Name: "foo", 501 Server: "https://127.0.0.1", 502 Namespaces: []string{"default", "kube-system"}, 503 } 504 barCluster := v1alpha1.Cluster{ 505 Name: "bar", 506 Server: "https://192.168.0.1", 507 Namespaces: []string{"default", "kube-system"}, 508 } 509 bazCluster := v1alpha1.Cluster{ 510 Name: "test/ing", 511 Server: "https://testing.com", 512 Namespaces: []string{"default", "kube-system"}, 513 } 514 515 mockClusterList := v1alpha1.ClusterList{ 516 ListMeta: v1.ListMeta{}, 517 Items: []v1alpha1.Cluster{fooCluster, barCluster, bazCluster}, 518 } 519 520 db.On("ListClusters", mock.Anything).Return(&mockClusterList, nil) 521 522 s := NewServer(db, newNoopEnforcer(), newServerInMemoryCache(), &kubetest.MockKubectlCmd{}) 523 524 tests := []struct { 525 name string 526 q *cluster.ClusterQuery 527 want *appv1.ClusterList 528 wantErr bool 529 }{ 530 { 531 name: "filter by name", 532 q: &clusterapi.ClusterQuery{ 533 Name: fooCluster.Name, 534 }, 535 want: &v1alpha1.ClusterList{ 536 ListMeta: v1.ListMeta{}, 537 Items: []v1alpha1.Cluster{fooCluster}, 538 }, 539 }, 540 { 541 name: "filter by server", 542 q: &clusterapi.ClusterQuery{ 543 Server: barCluster.Server, 544 }, 545 want: &v1alpha1.ClusterList{ 546 ListMeta: v1.ListMeta{}, 547 Items: []v1alpha1.Cluster{barCluster}, 548 }, 549 }, 550 { 551 name: "filter by id - name", 552 q: &clusterapi.ClusterQuery{ 553 Id: &clusterapi.ClusterID{ 554 Type: "name", 555 Value: fooCluster.Name, 556 }, 557 }, 558 want: &v1alpha1.ClusterList{ 559 ListMeta: v1.ListMeta{}, 560 Items: []v1alpha1.Cluster{fooCluster}, 561 }, 562 }, 563 { 564 name: "filter by id - name_escaped", 565 q: &clusterapi.ClusterQuery{ 566 Id: &clusterapi.ClusterID{ 567 Type: "name_escaped", 568 Value: "test%2fing", 569 }, 570 }, 571 want: &v1alpha1.ClusterList{ 572 ListMeta: v1.ListMeta{}, 573 Items: []v1alpha1.Cluster{bazCluster}, 574 }, 575 }, 576 { 577 name: "filter by id - server", 578 q: &clusterapi.ClusterQuery{ 579 Id: &clusterapi.ClusterID{ 580 Type: "server", 581 Value: barCluster.Server, 582 }, 583 }, 584 want: &v1alpha1.ClusterList{ 585 ListMeta: v1.ListMeta{}, 586 Items: []v1alpha1.Cluster{barCluster}, 587 }, 588 }, 589 } 590 for _, tt := range tests { 591 tt := tt 592 593 t.Run(tt.name, func(t *testing.T) { 594 t.Parallel() 595 596 got, err := s.List(context.Background(), tt.q) 597 if (err != nil) != tt.wantErr { 598 t.Errorf("Server.List() error = %v, wantErr %v", err, tt.wantErr) 599 return 600 } 601 if !reflect.DeepEqual(got, tt.want) { 602 t.Errorf("Server.List() = %v, want %v", got, tt.want) 603 } 604 }) 605 } 606 }