github.com/argoproj/argo-cd/v3@v3.2.1/util/db/db_test.go (about) 1 package db 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 "google.golang.org/grpc/codes" 10 "google.golang.org/grpc/status" 11 appsv1 "k8s.io/api/apps/v1" 12 corev1 "k8s.io/api/core/v1" 13 "k8s.io/apimachinery/pkg/api/errors" 14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 "k8s.io/apimachinery/pkg/runtime" 16 "k8s.io/client-go/kubernetes/fake" 17 18 "github.com/argoproj/argo-cd/v3/common" 19 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 20 "github.com/argoproj/argo-cd/v3/util/settings" 21 ) 22 23 const ( 24 testNamespace = "default" 25 ) 26 27 func getClientset(objects ...runtime.Object) *fake.Clientset { 28 secret := corev1.Secret{ 29 ObjectMeta: metav1.ObjectMeta{ 30 Name: "argocd-secret", 31 Namespace: testNamespace, 32 }, 33 Data: map[string][]byte{ 34 "admin.password": []byte("test"), 35 "server.secretkey": []byte("test"), 36 }, 37 } 38 cm := corev1.ConfigMap{ 39 ObjectMeta: metav1.ObjectMeta{ 40 Name: "argocd-cm", 41 Namespace: testNamespace, 42 Labels: map[string]string{ 43 "app.kubernetes.io/part-of": "argocd", 44 }, 45 }, 46 } 47 return fake.NewClientset(append(objects, &cm, &secret)...) 48 } 49 50 func TestCreateRepository(t *testing.T) { 51 clientset := getClientset() 52 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 53 54 repo, err := db.CreateRepository(t.Context(), &v1alpha1.Repository{ 55 Repo: "https://github.com/argoproj/argocd-example-apps", 56 Username: "test-username", 57 Password: "test-password", 58 }) 59 require.NoError(t, err) 60 assert.Equal(t, "https://github.com/argoproj/argocd-example-apps", repo.Repo) 61 62 secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), RepoURLToSecretName(repoSecretPrefix, repo.Repo, ""), metav1.GetOptions{}) 63 require.NoError(t, err) 64 65 assert.Equal(t, common.AnnotationValueManagedByArgoCD, secret.Annotations[common.AnnotationKeyManagedBy]) 66 assert.Equal(t, "test-username", string(secret.Data[username])) 67 assert.Equal(t, "test-password", string(secret.Data[password])) 68 assert.Empty(t, secret.Data[sshPrivateKey]) 69 } 70 71 func TestCreateProjectScopedRepository(t *testing.T) { 72 clientset := getClientset() 73 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 74 75 repo, err := db.CreateRepository(t.Context(), &v1alpha1.Repository{ 76 Repo: "https://github.com/argoproj/argocd-example-apps", 77 Username: "test-username", 78 Password: "test-password", 79 Project: "test-project", 80 }) 81 require.NoError(t, err) 82 83 otherRepo, err := db.CreateRepository(t.Context(), &v1alpha1.Repository{ 84 Repo: "https://github.com/argoproj/argocd-example-apps", 85 Username: "other-username", 86 Password: "other-password", 87 Project: "other-project", 88 }) 89 require.NoError(t, err) 90 91 _, err = db.CreateRepository(t.Context(), &v1alpha1.Repository{ 92 Repo: "https://github.com/argoproj/argocd-example-apps", 93 Username: "wrong-username", 94 Password: "wrong-password", 95 }) 96 require.NoError(t, err) 97 98 assert.Equal(t, "https://github.com/argoproj/argocd-example-apps", repo.Repo) 99 100 secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), RepoURLToSecretName(repoSecretPrefix, repo.Repo, "test-project"), metav1.GetOptions{}) 101 require.NoError(t, err) 102 103 assert.Equal(t, common.AnnotationValueManagedByArgoCD, secret.Annotations[common.AnnotationKeyManagedBy]) 104 assert.Equal(t, "test-username", string(secret.Data[username])) 105 assert.Equal(t, "test-password", string(secret.Data[password])) 106 assert.Equal(t, "test-project", string(secret.Data[project])) 107 assert.Empty(t, secret.Data[sshPrivateKey]) 108 109 secret, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), RepoURLToSecretName(repoSecretPrefix, otherRepo.Repo, "other-project"), metav1.GetOptions{}) 110 require.NoError(t, err) 111 assert.Equal(t, common.AnnotationValueManagedByArgoCD, secret.Annotations[common.AnnotationKeyManagedBy]) 112 assert.Equal(t, "other-username", string(secret.Data[username])) 113 assert.Equal(t, "other-password", string(secret.Data[password])) 114 assert.Equal(t, "other-project", string(secret.Data[project])) 115 assert.Empty(t, secret.Data[sshPrivateKey]) 116 } 117 118 func TestCreateRepoCredentials(t *testing.T) { 119 clientset := getClientset() 120 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 121 122 creds, err := db.CreateRepositoryCredentials(t.Context(), &v1alpha1.RepoCreds{ 123 URL: "https://github.com/argoproj/", 124 Username: "test-username", 125 Password: "test-password", 126 }) 127 require.NoError(t, err) 128 assert.Equal(t, "https://github.com/argoproj/", creds.URL) 129 130 secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), RepoURLToSecretName(credSecretPrefix, creds.URL, ""), metav1.GetOptions{}) 131 require.NoError(t, err) 132 133 assert.Equal(t, common.AnnotationValueManagedByArgoCD, secret.Annotations[common.AnnotationKeyManagedBy]) 134 assert.Equal(t, "test-username", string(secret.Data[username])) 135 assert.Equal(t, "test-password", string(secret.Data[password])) 136 assert.Empty(t, secret.Data[sshPrivateKey]) 137 138 created, err := db.CreateRepository(t.Context(), &v1alpha1.Repository{ 139 Repo: "https://github.com/argoproj/argo-cd", 140 }) 141 require.NoError(t, err) 142 assert.Equal(t, "https://github.com/argoproj/argo-cd", created.Repo) 143 144 // There seems to be a race or some other hiccup in the fake K8s clientset used for this test. 145 // Just give it a little time to settle. 146 time.Sleep(1 * time.Second) 147 148 repo, err := db.GetRepository(t.Context(), created.Repo, "") 149 require.NoError(t, err) 150 assert.Equal(t, "test-username", repo.Username) 151 assert.Equal(t, "test-password", repo.Password) 152 } 153 154 func TestCreateWriteRepoCredentials(t *testing.T) { 155 clientset := getClientset() 156 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 157 158 creds, err := db.CreateWriteRepositoryCredentials(t.Context(), &v1alpha1.RepoCreds{ 159 URL: "https://github.com/argoproj/", 160 Username: "test-username", 161 Password: "test-password", 162 }) 163 require.NoError(t, err) 164 assert.Equal(t, "https://github.com/argoproj/", creds.URL) 165 166 secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), RepoURLToSecretName(credSecretPrefix, creds.URL, ""), metav1.GetOptions{}) 167 require.NoError(t, err) 168 169 assert.Equal(t, common.AnnotationValueManagedByArgoCD, secret.Annotations[common.AnnotationKeyManagedBy]) 170 assert.Equal(t, "test-username", string(secret.Data[username])) 171 assert.Equal(t, "test-password", string(secret.Data[password])) 172 assert.Empty(t, secret.Data[sshPrivateKey]) 173 174 created, err := db.CreateWriteRepository(t.Context(), &v1alpha1.Repository{ 175 Repo: "https://github.com/argoproj/argo-cd", 176 }) 177 require.NoError(t, err) 178 assert.Equal(t, "https://github.com/argoproj/argo-cd", created.Repo) 179 180 repo, err := db.GetWriteRepository(t.Context(), created.Repo, "") 181 require.NoError(t, err) 182 assert.Equal(t, "test-username", repo.Username) 183 assert.Equal(t, "test-password", repo.Password) 184 } 185 186 func TestGetRepositoryCredentials(t *testing.T) { 187 clientset := getClientset() 188 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 189 _, err := db.CreateRepositoryCredentials(t.Context(), &v1alpha1.RepoCreds{ 190 URL: "https://secured", 191 Username: "test-username", 192 Password: "test-password", 193 }) 194 require.NoError(t, err) 195 196 tests := []struct { 197 name string 198 repoURL string 199 want *v1alpha1.RepoCreds 200 }{ 201 { 202 name: "TestUnknownRepo", 203 repoURL: "https://unknown/repo", 204 want: nil, 205 }, 206 { 207 name: "TestKnownRepo", 208 repoURL: "https://known/repo", 209 want: nil, 210 }, 211 { 212 name: "TestSecuredRepo", 213 repoURL: "https://secured/repo", 214 want: &v1alpha1.RepoCreds{URL: "https://secured", Username: "test-username", Password: "test-password"}, 215 }, 216 { 217 name: "TestMissingRepo", 218 repoURL: "https://missing/repo", 219 want: nil, 220 }, 221 } 222 for _, tt := range tests { 223 t.Run(tt.name, func(t *testing.T) { 224 got, err := db.GetRepositoryCredentials(t.Context(), tt.repoURL) 225 226 require.NoError(t, err) 227 assert.Equal(t, tt.want, got) 228 }) 229 } 230 } 231 232 func TestGetWriteRepositoryCredentials(t *testing.T) { 233 clientset := getClientset() 234 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 235 _, err := db.CreateWriteRepositoryCredentials(t.Context(), &v1alpha1.RepoCreds{ 236 URL: "https://secured", 237 Username: "test-username", 238 Password: "test-password", 239 }) 240 require.NoError(t, err) 241 242 tests := []struct { 243 name string 244 repoURL string 245 want *v1alpha1.RepoCreds 246 }{ 247 { 248 name: "TestUnknownRepo", 249 repoURL: "https://unknown/repo", 250 want: nil, 251 }, 252 { 253 name: "TestKnownRepo", 254 repoURL: "https://known/repo", 255 want: nil, 256 }, 257 { 258 name: "TestSecuredRepo", 259 repoURL: "https://secured/repo", 260 want: &v1alpha1.RepoCreds{URL: "https://secured", Username: "test-username", Password: "test-password"}, 261 }, 262 { 263 name: "TestMissingRepo", 264 repoURL: "https://missing/repo", 265 want: nil, 266 }, 267 } 268 for _, tt := range tests { 269 t.Run(tt.name, func(t *testing.T) { 270 got, err := db.GetWriteRepositoryCredentials(t.Context(), tt.repoURL) 271 272 require.NoError(t, err) 273 assert.Equal(t, tt.want, got) 274 }) 275 } 276 } 277 278 func TestCreateExistingRepository(t *testing.T) { 279 clientset := getClientset() 280 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 281 282 _, err := db.CreateRepository(t.Context(), &v1alpha1.Repository{ 283 Repo: "https://github.com/argoproj/argocd-example-apps", 284 Username: "test-username", 285 Password: "test-password", 286 }) 287 require.NoError(t, err) 288 289 _, err = db.CreateRepository(t.Context(), &v1alpha1.Repository{ 290 Repo: "https://github.com/argoproj/argocd-example-apps", 291 Username: "test-username", 292 Password: "test-password", 293 }) 294 require.Error(t, err) 295 assert.Equal(t, codes.AlreadyExists, status.Convert(err).Code()) 296 } 297 298 func TestGetRepository(t *testing.T) { 299 clientset := getClientset(&corev1.Secret{ 300 ObjectMeta: metav1.ObjectMeta{ 301 Namespace: testNamespace, 302 Name: "known-repo-secret", 303 Annotations: map[string]string{ 304 common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD, 305 }, 306 Labels: map[string]string{ 307 common.LabelKeySecretType: common.LabelValueSecretTypeRepository, 308 }, 309 }, 310 Data: map[string][]byte{ 311 "url": []byte("https://known/repo"), 312 }, 313 }, &corev1.Secret{ 314 ObjectMeta: metav1.ObjectMeta{ 315 Namespace: testNamespace, 316 Name: "secured-repo-secret", 317 Annotations: map[string]string{ 318 common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD, 319 }, 320 Labels: map[string]string{ 321 common.LabelKeySecretType: common.LabelValueSecretTypeRepository, 322 }, 323 }, 324 Data: map[string][]byte{ 325 "url": []byte("https://secured/repo"), 326 }, 327 }, &corev1.Secret{ 328 ObjectMeta: metav1.ObjectMeta{ 329 Namespace: testNamespace, 330 Name: "secured-repo-creds-secret", 331 Annotations: map[string]string{ 332 common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD, 333 }, 334 Labels: map[string]string{ 335 common.LabelKeySecretType: common.LabelValueSecretTypeRepoCreds, 336 }, 337 }, 338 Data: map[string][]byte{ 339 "url": []byte("https://secured"), 340 "username": []byte("test-username"), 341 "password": []byte("test-password"), 342 }, 343 }) 344 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 345 346 tests := []struct { 347 name string 348 repoURL string 349 want *v1alpha1.Repository 350 }{ 351 { 352 name: "TestUnknownRepo", 353 repoURL: "https://unknown/repo", 354 want: &v1alpha1.Repository{Repo: "https://unknown/repo"}, 355 }, 356 { 357 name: "TestKnownRepo", 358 repoURL: "https://known/repo", 359 want: &v1alpha1.Repository{Repo: "https://known/repo"}, 360 }, 361 { 362 name: "TestSecuredRepo", 363 repoURL: "https://secured/repo", 364 want: &v1alpha1.Repository{Repo: "https://secured/repo", Username: "test-username", Password: "test-password", InheritedCreds: true}, 365 }, 366 } 367 for _, tt := range tests { 368 t.Run(tt.name, func(t *testing.T) { 369 got, err := db.GetRepository(t.Context(), tt.repoURL, "") 370 require.NoError(t, err) 371 assert.Equal(t, tt.want, got) 372 }) 373 } 374 } 375 376 func TestGetWriteRepository(t *testing.T) { 377 clientset := getClientset(&corev1.Secret{ 378 ObjectMeta: metav1.ObjectMeta{ 379 Namespace: testNamespace, 380 Name: "known-repo-secret", 381 Annotations: map[string]string{ 382 common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD, 383 }, 384 Labels: map[string]string{ 385 common.LabelKeySecretType: common.LabelValueSecretTypeRepositoryWrite, 386 }, 387 }, 388 Data: map[string][]byte{ 389 "url": []byte("https://known/repo"), 390 }, 391 }, &corev1.Secret{ 392 ObjectMeta: metav1.ObjectMeta{ 393 Namespace: testNamespace, 394 Name: "secured-repo-secret", 395 Annotations: map[string]string{ 396 common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD, 397 }, 398 Labels: map[string]string{ 399 common.LabelKeySecretType: common.LabelValueSecretTypeRepositoryWrite, 400 }, 401 }, 402 Data: map[string][]byte{ 403 "url": []byte("https://secured/repo"), 404 }, 405 }, &corev1.Secret{ 406 ObjectMeta: metav1.ObjectMeta{ 407 Namespace: testNamespace, 408 Name: "secured-repo-creds-secret", 409 Annotations: map[string]string{ 410 common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD, 411 }, 412 Labels: map[string]string{ 413 common.LabelKeySecretType: common.LabelValueSecretTypeRepoCredsWrite, 414 }, 415 }, 416 Data: map[string][]byte{ 417 "url": []byte("https://secured"), 418 "username": []byte("test-username"), 419 "password": []byte("test-password"), 420 }, 421 }) 422 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 423 424 tests := []struct { 425 name string 426 repoURL string 427 want *v1alpha1.Repository 428 }{ 429 { 430 name: "TestUnknownRepo", 431 repoURL: "https://unknown/repo", 432 want: &v1alpha1.Repository{Repo: "https://unknown/repo"}, 433 }, 434 { 435 name: "TestKnownRepo", 436 repoURL: "https://known/repo", 437 want: &v1alpha1.Repository{Repo: "https://known/repo"}, 438 }, 439 { 440 name: "TestSecuredRepo", 441 repoURL: "https://secured/repo", 442 want: &v1alpha1.Repository{Repo: "https://secured/repo", Username: "test-username", Password: "test-password", InheritedCreds: true}, 443 }, 444 } 445 for _, tt := range tests { 446 t.Run(tt.name, func(t *testing.T) { 447 got, err := db.GetWriteRepository(t.Context(), tt.repoURL, "") 448 require.NoError(t, err) 449 assert.Equal(t, tt.want, got) 450 }) 451 } 452 } 453 454 func TestCreateClusterSuccessful(t *testing.T) { 455 server := "https://mycluster" 456 clientset := getClientset() 457 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 458 459 _, err := db.CreateCluster(t.Context(), &v1alpha1.Cluster{ 460 Server: server, 461 }) 462 require.NoError(t, err) 463 464 secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), "cluster-mycluster-3274446258", metav1.GetOptions{}) 465 require.NoError(t, err) 466 467 assert.Equal(t, server, string(secret.Data["server"])) 468 assert.Equal(t, common.AnnotationValueManagedByArgoCD, secret.Annotations[common.AnnotationKeyManagedBy]) 469 } 470 471 func TestDeleteClusterWithManagedSecret(t *testing.T) { 472 clusterURL := "https://mycluster" 473 clusterName := "cluster-mycluster-3274446258" 474 475 clientset := getClientset(&corev1.Secret{ 476 ObjectMeta: metav1.ObjectMeta{ 477 Name: clusterName, 478 Namespace: testNamespace, 479 Labels: map[string]string{ 480 common.LabelKeySecretType: common.LabelValueSecretTypeCluster, 481 }, 482 Annotations: map[string]string{ 483 common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD, 484 }, 485 }, 486 Data: map[string][]byte{ 487 "server": []byte(clusterURL), 488 "config": []byte("{}"), 489 }, 490 }) 491 492 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 493 err := db.DeleteCluster(t.Context(), clusterURL) 494 require.NoError(t, err) 495 496 _, err = clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), clusterName, metav1.GetOptions{}) 497 require.Error(t, err) 498 499 assert.True(t, errors.IsNotFound(err)) 500 } 501 502 func TestDeleteClusterWithUnmanagedSecret(t *testing.T) { 503 clusterURL := "https://mycluster" 504 clusterName := "mycluster-443" 505 506 clientset := getClientset(&corev1.Secret{ 507 ObjectMeta: metav1.ObjectMeta{ 508 Name: clusterName, 509 Namespace: testNamespace, 510 Labels: map[string]string{ 511 common.LabelKeySecretType: common.LabelValueSecretTypeCluster, 512 }, 513 }, 514 Data: map[string][]byte{ 515 "server": []byte(clusterURL), 516 "config": []byte("{}"), 517 }, 518 }) 519 520 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 521 err := db.DeleteCluster(t.Context(), clusterURL) 522 require.NoError(t, err) 523 524 secret, err := clientset.CoreV1().Secrets(testNamespace).Get(t.Context(), clusterName, metav1.GetOptions{}) 525 require.NoError(t, err) 526 527 assert.Empty(t, secret.Labels) 528 } 529 530 func TestFuzzyEquivalence(t *testing.T) { 531 clientset := getClientset() 532 ctx := t.Context() 533 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 534 535 repo, err := db.CreateRepository(ctx, &v1alpha1.Repository{ 536 Repo: "https://github.com/argoproj/argocd-example-apps", 537 }) 538 require.NoError(t, err) 539 assert.Equal(t, "https://github.com/argoproj/argocd-example-apps", repo.Repo) 540 541 repo, err = db.CreateRepository(ctx, &v1alpha1.Repository{ 542 Repo: "https://github.com/argoproj/argocd-example-apps.git", 543 }) 544 require.ErrorContains(t, err, "already exists") 545 assert.Nil(t, repo) 546 547 repo, err = db.CreateRepository(ctx, &v1alpha1.Repository{ 548 Repo: "https://github.com/argoproj/argocd-example-APPS", 549 }) 550 require.ErrorContains(t, err, "already exists") 551 assert.Nil(t, repo) 552 553 repo, err = db.GetRepository(ctx, "https://github.com/argoproj/argocd-example-APPS", "") 554 require.NoError(t, err) 555 assert.Equal(t, "https://github.com/argoproj/argocd-example-apps", repo.Repo) 556 } 557 558 func TestGetApplicationControllerReplicas(t *testing.T) { 559 clientset := getClientset() 560 expectedReplicas := int32(2) 561 t.Setenv(common.EnvControllerReplicas, "2") 562 db := NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 563 replicas := db.GetApplicationControllerReplicas() 564 assert.Equal(t, int(expectedReplicas), replicas) 565 566 expectedReplicas = int32(3) 567 clientset = getClientset(&appsv1.Deployment{ 568 ObjectMeta: metav1.ObjectMeta{ 569 Name: common.ApplicationController, 570 Namespace: testNamespace, 571 }, 572 Spec: appsv1.DeploymentSpec{ 573 Replicas: &expectedReplicas, 574 }, 575 }) 576 t.Setenv(common.EnvControllerReplicas, "2") 577 db = NewDB(testNamespace, settings.NewSettingsManager(t.Context(), clientset, testNamespace), clientset) 578 replicas = db.GetApplicationControllerReplicas() 579 assert.Equal(t, int(expectedReplicas), replicas) 580 }