github.com/argoproj/argo-cd/v3@v3.2.1/server/repository/repository_test.go (about) 1 package repository 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 "time" 8 9 "github.com/golang-jwt/jwt/v5" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/mock" 12 "github.com/stretchr/testify/require" 13 "google.golang.org/grpc/codes" 14 "google.golang.org/grpc/status" 15 corev1 "k8s.io/api/core/v1" 16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 "k8s.io/apimachinery/pkg/runtime" 18 "k8s.io/client-go/kubernetes/fake" 19 k8scache "k8s.io/client-go/tools/cache" 20 21 "github.com/argoproj/argo-cd/v3/common" 22 "github.com/argoproj/argo-cd/v3/pkg/apiclient/repository" 23 appsv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 24 fakeapps "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/fake" 25 appinformer "github.com/argoproj/argo-cd/v3/pkg/client/informers/externalversions" 26 applisters "github.com/argoproj/argo-cd/v3/pkg/client/listers/application/v1alpha1" 27 "github.com/argoproj/argo-cd/v3/reposerver/apiclient" 28 "github.com/argoproj/argo-cd/v3/reposerver/apiclient/mocks" 29 "github.com/argoproj/argo-cd/v3/server/cache" 30 "github.com/argoproj/argo-cd/v3/util/assets" 31 cacheutil "github.com/argoproj/argo-cd/v3/util/cache" 32 appstatecache "github.com/argoproj/argo-cd/v3/util/cache/appstate" 33 "github.com/argoproj/argo-cd/v3/util/db" 34 dbmocks "github.com/argoproj/argo-cd/v3/util/db/mocks" 35 "github.com/argoproj/argo-cd/v3/util/rbac" 36 "github.com/argoproj/argo-cd/v3/util/settings" 37 38 "github.com/argoproj/argo-cd/v3/pkg/apis/application" 39 ) 40 41 const testNamespace = "default" 42 43 var ( 44 argocdCM = corev1.ConfigMap{ 45 ObjectMeta: metav1.ObjectMeta{ 46 Namespace: testNamespace, 47 Name: "argocd-cm", 48 Labels: map[string]string{ 49 "app.kubernetes.io/part-of": "argocd", 50 }, 51 }, 52 } 53 argocdSecret = corev1.Secret{ 54 ObjectMeta: metav1.ObjectMeta{ 55 Name: "argocd-secret", 56 Namespace: testNamespace, 57 }, 58 Data: map[string][]byte{ 59 "admin.password": []byte("test"), 60 "server.secretkey": []byte("test"), 61 }, 62 } 63 defaultProj = &appsv1.AppProject{ 64 TypeMeta: metav1.TypeMeta{ 65 Kind: application.AppProjectKind, 66 APIVersion: "argoproj.io/v1alpha1", 67 }, 68 ObjectMeta: metav1.ObjectMeta{ 69 Name: "default", 70 Namespace: testNamespace, 71 }, 72 Spec: appsv1.AppProjectSpec{ 73 SourceRepos: []string{"*"}, 74 Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}}, 75 }, 76 } 77 78 defaultProjNoSources = &appsv1.AppProject{ 79 TypeMeta: metav1.TypeMeta{ 80 Kind: application.AppProjectKind, 81 APIVersion: "argoproj.io/v1alpha1", 82 }, 83 ObjectMeta: metav1.ObjectMeta{ 84 Name: "default", 85 Namespace: testNamespace, 86 }, 87 Spec: appsv1.AppProjectSpec{ 88 SourceRepos: []string{}, 89 Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}}, 90 }, 91 } 92 fakeRepo = appsv1.Repository{ 93 Repo: "https://test", 94 Type: "test", 95 Name: "test", 96 Username: "argo", 97 Insecure: false, 98 EnableLFS: false, 99 EnableOCI: false, 100 Proxy: "test", 101 Project: "argocd", 102 InheritedCreds: true, 103 } 104 guestbookApp = &appsv1.Application{ 105 TypeMeta: metav1.TypeMeta{ 106 Kind: application.ApplicationKind, 107 APIVersion: "argoproj.io/v1alpha1", 108 }, 109 ObjectMeta: metav1.ObjectMeta{ 110 Name: "guestbook", 111 Namespace: testNamespace, 112 }, 113 Spec: appsv1.ApplicationSpec{ 114 Project: "default", 115 Source: &appsv1.ApplicationSource{ 116 RepoURL: "https://test", 117 TargetRevision: "HEAD", 118 Helm: &appsv1.ApplicationSourceHelm{ 119 ValueFiles: []string{"values.yaml"}, 120 }, 121 }, 122 }, 123 Status: appsv1.ApplicationStatus{ 124 History: appsv1.RevisionHistories{ 125 { 126 Revision: "abcdef123567", 127 Source: appsv1.ApplicationSource{ 128 RepoURL: "https://test", 129 TargetRevision: "HEAD", 130 Helm: &appsv1.ApplicationSourceHelm{ 131 ValueFiles: []string{"values-old.yaml"}, 132 }, 133 }, 134 }, 135 }, 136 }, 137 } 138 multiSourceApp001AppName = "msa-two-helm-types" 139 multiSourceApp001 = &appsv1.Application{ 140 TypeMeta: metav1.TypeMeta{ 141 Kind: application.ApplicationKind, 142 APIVersion: "argoproj.io/v1alpha1", 143 }, 144 ObjectMeta: metav1.ObjectMeta{ 145 Name: multiSourceApp001AppName, 146 Namespace: testNamespace, 147 }, 148 Spec: appsv1.ApplicationSpec{ 149 Project: "default", 150 Sources: []appsv1.ApplicationSource{ 151 { 152 RepoURL: "https://helm.elastic.co", 153 TargetRevision: "7.7.0", 154 Chart: "elasticsearch", 155 Helm: &appsv1.ApplicationSourceHelm{ 156 ValueFiles: []string{"values.yaml"}, 157 }, 158 }, 159 { 160 RepoURL: "https://helm.elastic.co", 161 TargetRevision: "7.6.0", 162 Chart: "elasticsearch", 163 Helm: &appsv1.ApplicationSourceHelm{ 164 ValueFiles: []string{"values.yaml"}, 165 }, 166 }, 167 }, 168 }, 169 Status: appsv1.ApplicationStatus{ 170 History: appsv1.RevisionHistories{ 171 { 172 ID: 1, 173 Revisions: []string{ 174 "abcdef123567", 175 }, 176 Sources: []appsv1.ApplicationSource{ 177 { 178 RepoURL: "https://helm.elastic.co", 179 TargetRevision: "7.6.0", 180 Helm: &appsv1.ApplicationSourceHelm{ 181 ValueFiles: []string{"values-old.yaml"}, 182 }, 183 }, 184 }, 185 }, 186 }, 187 }, 188 } 189 multiSourceApp002AppName = "msa-one-plugin-one-helm" 190 multiSourceApp002 = &appsv1.Application{ 191 TypeMeta: metav1.TypeMeta{ 192 Kind: application.ApplicationKind, 193 APIVersion: "argoproj.io/v1alpha1", 194 }, 195 ObjectMeta: metav1.ObjectMeta{ 196 Name: multiSourceApp002AppName, 197 Namespace: testNamespace, 198 }, 199 Spec: appsv1.ApplicationSpec{ 200 Project: "default", 201 Sources: []appsv1.ApplicationSource{ 202 { 203 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 204 Path: "sock-shop", 205 TargetRevision: "HEAD", 206 }, 207 { 208 RepoURL: "https://helm.elastic.co", 209 TargetRevision: "7.7.0", 210 Chart: "elasticsearch", 211 Helm: &appsv1.ApplicationSourceHelm{ 212 ValueFiles: []string{"values.yaml"}, 213 }, 214 }, 215 }, 216 }, 217 Status: appsv1.ApplicationStatus{ 218 History: appsv1.RevisionHistories{ 219 { 220 Revision: "HEAD", 221 Sources: []appsv1.ApplicationSource{ 222 { 223 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 224 TargetRevision: "1.0.0", 225 }, 226 }, 227 }, 228 }, 229 }, 230 } 231 ) 232 233 func newAppAndProjLister(objects ...runtime.Object) (applisters.ApplicationLister, k8scache.SharedIndexInformer) { 234 fakeAppsClientset := fakeapps.NewSimpleClientset(objects...) 235 factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(""), appinformer.WithTweakListOptions(func(_ *metav1.ListOptions) {})) 236 projInformer := factory.Argoproj().V1alpha1().AppProjects() 237 appsInformer := factory.Argoproj().V1alpha1().Applications() 238 for _, obj := range objects { 239 switch obj.(type) { 240 case *appsv1.AppProject: 241 _ = projInformer.Informer().GetStore().Add(obj) 242 case *appsv1.Application: 243 _ = appsInformer.Informer().GetStore().Add(obj) 244 } 245 } 246 appLister := appsInformer.Lister() 247 return appLister, projInformer.Informer() 248 } 249 250 func Test_createRBACObject(t *testing.T) { 251 object := createRBACObject("test-prj", "test-repo") 252 assert.Equal(t, "test-prj/test-repo", object) 253 objectWithoutPrj := createRBACObject("", "test-repo") 254 assert.Equal(t, "test-repo", objectWithoutPrj) 255 } 256 257 func TestRepositoryServer(t *testing.T) { 258 kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret) 259 settingsMgr := settings.NewSettingsManager(t.Context(), kubeclientset, testNamespace) 260 enforcer := newEnforcer(kubeclientset) 261 appLister, projInformer := newAppAndProjLister(defaultProj) 262 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 263 264 t.Run("Test_getRepo", func(t *testing.T) { 265 repoServerClient := mocks.RepoServerServiceClient{} 266 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 267 268 s := NewServer(&repoServerClientset, argoDB, enforcer, nil, appLister, projInformer, testNamespace, settingsMgr, false) 269 url := "https://test" 270 repo, _ := s.getRepo(t.Context(), url, "") 271 assert.Equal(t, repo.Repo, url) 272 }) 273 274 t.Run("Test_validateAccess", func(t *testing.T) { 275 repoServerClient := mocks.RepoServerServiceClient{} 276 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 277 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 278 279 s := NewServer(&repoServerClientset, argoDB, enforcer, nil, appLister, projInformer, testNamespace, settingsMgr, false) 280 url := "https://test" 281 _, err := s.ValidateAccess(t.Context(), &repository.RepoAccessQuery{ 282 Repo: url, 283 }) 284 require.NoError(t, err) 285 }) 286 287 t.Run("Test_Get", func(t *testing.T) { 288 repoServerClient := mocks.RepoServerServiceClient{} 289 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 290 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 291 292 url := "https://test" 293 db := &dbmocks.ArgoDB{} 294 db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: url}}, nil) 295 db.On("GetRepository", t.Context(), url, "").Return(&appsv1.Repository{Repo: url}, nil) 296 db.On("RepositoryExists", t.Context(), url, "").Return(true, nil) 297 298 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false) 299 repo, err := s.Get(t.Context(), &repository.RepoQuery{ 300 Repo: url, 301 }) 302 require.NoError(t, err) 303 assert.Equal(t, repo.Repo, url) 304 }) 305 306 t.Run("Test_GetInherited", func(t *testing.T) { 307 repoServerClient := mocks.RepoServerServiceClient{} 308 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 309 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 310 311 url := "https://test" 312 db := &dbmocks.ArgoDB{} 313 testRepo := &appsv1.Repository{ 314 Repo: url, 315 Type: "git", 316 Username: "", 317 InheritedCreds: true, 318 } 319 db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{testRepo}, nil) 320 db.On("GetRepository", t.Context(), url, "").Return(testRepo, nil) 321 db.On("RepositoryExists", t.Context(), url, "").Return(true, nil) 322 323 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false) 324 repo, err := s.Get(t.Context(), &repository.RepoQuery{ 325 Repo: url, 326 }) 327 require.NoError(t, err) 328 329 testRepo.ConnectionState = repo.ConnectionState // overwrite connection state on our test object to simplify comparison below 330 331 assert.Equal(t, testRepo, repo) 332 }) 333 334 t.Run("Test_GetWithErrorShouldReturn403", func(t *testing.T) { 335 repoServerClient := mocks.RepoServerServiceClient{} 336 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 337 338 url := "https://test" 339 db := &dbmocks.ArgoDB{} 340 db.On("ListRepositories", t.Context()).Return(nil, nil) 341 db.On("GetRepository", t.Context(), url, "").Return(nil, errors.New("some error")) 342 db.On("RepositoryExists", t.Context(), url, "").Return(true, nil) 343 344 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false) 345 repo, err := s.Get(t.Context(), &repository.RepoQuery{ 346 Repo: url, 347 }) 348 assert.Nil(t, repo) 349 assert.Equal(t, err, common.PermissionDeniedAPIError) 350 }) 351 352 t.Run("Test_GetWithNotExistRepoShouldReturn404", func(t *testing.T) { 353 repoServerClient := mocks.RepoServerServiceClient{} 354 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 355 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 356 357 url := "https://test" 358 db := &dbmocks.ArgoDB{} 359 db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: url}}, nil) 360 db.On("GetRepository", t.Context(), url, "").Return(&appsv1.Repository{Repo: url}, nil) 361 db.On("RepositoryExists", t.Context(), url, "").Return(false, nil) 362 363 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false) 364 repo, err := s.Get(t.Context(), &repository.RepoQuery{ 365 Repo: url, 366 }) 367 assert.Nil(t, repo) 368 assert.EqualError(t, err, "rpc error: code = NotFound desc = repo 'https://test' not found") 369 }) 370 371 t.Run("Test_GetRepoIsSanitized", func(t *testing.T) { 372 repoServerClient := mocks.RepoServerServiceClient{} 373 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 374 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 375 376 url := "https://test" 377 db := &dbmocks.ArgoDB{} 378 db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: url, Username: "test", Password: "it's a secret", GitHubAppEnterpriseBaseURL: "https://ghe.example.com/api/v3", GithubAppId: 123456, GithubAppInstallationId: 789}}, nil) 379 db.On("GetRepository", t.Context(), url, "").Return(&appsv1.Repository{Repo: url, Username: "test", Password: "it's a secret"}, nil) 380 db.On("RepositoryExists", t.Context(), url, "").Return(true, nil) 381 382 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false) 383 repo, err := s.Get(t.Context(), &repository.RepoQuery{ 384 Repo: url, 385 }) 386 require.NoError(t, err) 387 assert.Equal(t, "https://test", repo.Repo) 388 assert.Equal(t, "https://ghe.example.com/api/v3", repo.GitHubAppEnterpriseBaseURL) 389 assert.Equal(t, int64(123456), repo.GithubAppId) 390 assert.Equal(t, int64(789), repo.GithubAppInstallationId) 391 assert.Empty(t, repo.Password) 392 }) 393 394 t.Run("Test_GetRepoIsNormalized", func(t *testing.T) { 395 repoServerClient := mocks.RepoServerServiceClient{} 396 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 397 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 398 399 url := "https://test" 400 db := &dbmocks.ArgoDB{} 401 db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: url}}, nil) 402 db.On("GetRepository", t.Context(), url, "").Return(&appsv1.Repository{Repo: url, Username: "test"}, nil) 403 db.On("RepositoryExists", t.Context(), url, "").Return(true, nil) 404 405 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false) 406 repo, err := s.Get(t.Context(), &repository.RepoQuery{ 407 Repo: url, 408 }) 409 require.NoError(t, err) 410 assert.Equal(t, "https://test", repo.Repo) 411 assert.Equal(t, common.DefaultRepoType, repo.Type) 412 }) 413 414 t.Run("Test_GetRepoHasConnectionState", func(t *testing.T) { 415 repoServerClient := mocks.RepoServerServiceClient{} 416 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{ 417 VerifiedRepository: true, 418 }, nil) 419 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 420 421 url := "https://test" 422 db := &dbmocks.ArgoDB{} 423 db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: url}}, nil) 424 db.On("GetRepository", t.Context(), url, "").Return(&appsv1.Repository{Repo: url}, nil) 425 db.On("RepositoryExists", t.Context(), url, "").Return(true, nil) 426 427 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false) 428 repo, err := s.Get(t.Context(), &repository.RepoQuery{ 429 Repo: url, 430 }) 431 require.NoError(t, err) 432 require.NotNil(t, repo.ConnectionState) 433 assert.Equal(t, appsv1.ConnectionStatusSuccessful, repo.ConnectionState.Status) 434 }) 435 436 t.Run("Test_CreateRepositoryWithoutUpsert", func(t *testing.T) { 437 repoServerClient := mocks.RepoServerServiceClient{} 438 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 439 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 440 441 db := &dbmocks.ArgoDB{} 442 db.On("GetRepository", t.Context(), "test").Return(nil, errors.New("not found")) 443 db.On("CreateRepository", t.Context(), mock.Anything).Return(&apiclient.TestRepositoryResponse{}).Return(&appsv1.Repository{ 444 Repo: "repo", 445 Project: "proj", 446 }, nil) 447 448 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false) 449 repo, err := s.CreateRepository(t.Context(), &repository.RepoCreateRequest{ 450 Repo: &appsv1.Repository{ 451 Repo: "test", 452 Username: "test", 453 }, 454 }) 455 require.NoError(t, err) 456 assert.Equal(t, "repo", repo.Repo) 457 }) 458 459 t.Run("Test_CreateRepositoryWithUpsert", func(t *testing.T) { 460 repoServerClient := mocks.RepoServerServiceClient{} 461 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 462 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 463 464 r := &appsv1.Repository{ 465 Repo: "test", 466 Username: "test", 467 } 468 469 db := &dbmocks.ArgoDB{} 470 db.On("GetRepository", t.Context(), "test", "").Return(&appsv1.Repository{ 471 Repo: "test", 472 Username: "test", 473 }, nil) 474 db.On("CreateRepository", t.Context(), mock.Anything).Return(nil, status.Errorf(codes.AlreadyExists, "repository already exists")) 475 db.On("UpdateRepository", t.Context(), mock.Anything).Return(r, nil) 476 477 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false) 478 repo, err := s.CreateRepository(t.Context(), &repository.RepoCreateRequest{ 479 Repo: r, 480 Upsert: true, 481 }) 482 483 require.NoError(t, err) 484 require.NotNil(t, repo) 485 assert.Equal(t, "test", repo.Repo) 486 }) 487 488 t.Run("Test_ListRepositories", func(t *testing.T) { 489 repoServerClient := mocks.RepoServerServiceClient{} 490 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 491 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 492 enforcer := newEnforcer(kubeclientset) 493 494 url := "https://test" 495 db := &dbmocks.ArgoDB{} 496 db.On("GetRepository", t.Context(), url, "argocd").Return(nil, nil) 497 db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(nil, nil) 498 db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{&fakeRepo, &fakeRepo}, nil) 499 500 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false) 501 resp, err := s.ListRepositories(t.Context(), &repository.RepoQuery{}) 502 require.NoError(t, err) 503 assert.Len(t, resp.Items, 2) 504 }) 505 } 506 507 func TestRepositoryServerListApps(t *testing.T) { 508 kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret) 509 settingsMgr := settings.NewSettingsManager(t.Context(), kubeclientset, testNamespace) 510 511 t.Run("Test_WithoutAppCreateUpdatePrivileges", func(t *testing.T) { 512 repoServerClient := mocks.RepoServerServiceClient{} 513 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 514 enforcer := newEnforcer(kubeclientset) 515 enforcer.SetDefaultRole("role:readonly") 516 517 url := "https://test" 518 db := &dbmocks.ArgoDB{} 519 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 520 appLister, projLister := newAppAndProjLister(defaultProj) 521 522 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 523 resp, err := s.ListApps(t.Context(), &repository.RepoAppsQuery{ 524 Repo: "https://test", 525 Revision: "HEAD", 526 AppName: "foo", 527 AppProject: "default", 528 }) 529 assert.Nil(t, resp) 530 assert.Equal(t, err, common.PermissionDeniedAPIError) 531 }) 532 533 t.Run("Test_WithAppCreateUpdatePrivileges", func(t *testing.T) { 534 repoServerClient := mocks.RepoServerServiceClient{} 535 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 536 enforcer := newEnforcer(kubeclientset) 537 enforcer.SetDefaultRole("role:admin") 538 appLister, projLister := newAppAndProjLister(defaultProj) 539 540 url := "https://test" 541 db := &dbmocks.ArgoDB{} 542 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 543 db.On("GetProjectRepositories", "default").Return(nil, nil) 544 db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil) 545 repoServerClient.On("ListApps", t.Context(), mock.Anything).Return(&apiclient.AppList{ 546 Apps: map[string]string{ 547 "path/to/dir": "Kustomize", 548 }, 549 }, nil) 550 551 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 552 resp, err := s.ListApps(t.Context(), &repository.RepoAppsQuery{ 553 Repo: "https://test", 554 Revision: "HEAD", 555 AppName: "foo", 556 AppProject: "default", 557 }) 558 require.NoError(t, err) 559 require.Len(t, resp.Items, 1) 560 assert.Equal(t, "path/to/dir", resp.Items[0].Path) 561 assert.Equal(t, "Kustomize", resp.Items[0].Type) 562 }) 563 564 t.Run("Test_WithAppCreateUpdatePrivilegesRepoNotAllowed", func(t *testing.T) { 565 repoServerClient := mocks.RepoServerServiceClient{} 566 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 567 enforcer := newEnforcer(kubeclientset) 568 enforcer.SetDefaultRole("role:admin") 569 appLister, projLister := newAppAndProjLister(defaultProjNoSources) 570 571 url := "https://test" 572 db := &dbmocks.ArgoDB{} 573 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 574 db.On("GetProjectRepositories", "default").Return(nil, nil) 575 db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil) 576 repoServerClient.On("ListApps", t.Context(), mock.Anything).Return(&apiclient.AppList{ 577 Apps: map[string]string{ 578 "path/to/dir": "Kustomize", 579 }, 580 }, nil) 581 582 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 583 resp, err := s.ListApps(t.Context(), &repository.RepoAppsQuery{ 584 Repo: "https://test", 585 Revision: "HEAD", 586 AppName: "foo", 587 AppProject: "default", 588 }) 589 assert.Nil(t, resp) 590 require.Error(t, err, "repository 'https://test' not permitted in project 'default'") 591 }) 592 } 593 594 func TestRepositoryServerGetAppDetails(t *testing.T) { 595 kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret) 596 settingsMgr := settings.NewSettingsManager(t.Context(), kubeclientset, testNamespace) 597 598 t.Run("Test_WithoutRepoReadPrivileges", func(t *testing.T) { 599 repoServerClient := mocks.RepoServerServiceClient{} 600 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 601 enforcer := newEnforcer(kubeclientset) 602 enforcer.SetDefaultRole("") 603 604 url := "https://test" 605 db := &dbmocks.ArgoDB{} 606 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 607 appLister, projLister := newAppAndProjLister(defaultProj) 608 609 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 610 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 611 Source: &appsv1.ApplicationSource{ 612 RepoURL: url, 613 }, 614 AppName: "newapp", 615 AppProject: "default", 616 }) 617 assert.Nil(t, resp) 618 require.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: repositories, get, https://test") 619 }) 620 t.Run("Test_WithoutAppReadPrivileges", func(t *testing.T) { 621 repoServerClient := mocks.RepoServerServiceClient{} 622 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 623 enforcer := newEnforcer(kubeclientset) 624 _ = enforcer.SetUserPolicy("p, role:readrepos, repositories, get, *, allow") 625 enforcer.SetDefaultRole("role:readrepos") 626 627 url := "https://test" 628 db := &dbmocks.ArgoDB{} 629 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 630 appLister, projLister := newAppAndProjLister(defaultProj) 631 632 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 633 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 634 Source: &appsv1.ApplicationSource{ 635 RepoURL: url, 636 }, 637 AppName: "newapp", 638 AppProject: "default", 639 }) 640 assert.Nil(t, resp) 641 require.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, get, default/newapp") 642 }) 643 t.Run("Test_WithoutCreatePrivileges", func(t *testing.T) { 644 repoServerClient := mocks.RepoServerServiceClient{} 645 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 646 enforcer := newEnforcer(kubeclientset) 647 enforcer.SetDefaultRole("role:readonly") 648 649 url := "https://test" 650 db := &dbmocks.ArgoDB{} 651 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 652 appLister, projLister := newAppAndProjLister(defaultProj) 653 654 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 655 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 656 Source: &appsv1.ApplicationSource{ 657 RepoURL: url, 658 }, 659 AppName: "newapp", 660 AppProject: "default", 661 }) 662 assert.Nil(t, resp) 663 require.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, create, default/newapp") 664 }) 665 t.Run("Test_WithCreatePrivileges", func(t *testing.T) { 666 repoServerClient := mocks.RepoServerServiceClient{} 667 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 668 enforcer := newEnforcer(kubeclientset) 669 670 url := "https://test" 671 db := &dbmocks.ArgoDB{} 672 db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(nil, nil) 673 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 674 db.On("GetProjectRepositories", "default").Return(nil, nil) 675 db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil) 676 expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"} 677 repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil) 678 appLister, projLister := newAppAndProjLister(defaultProj) 679 680 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 681 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 682 Source: &appsv1.ApplicationSource{ 683 RepoURL: url, 684 }, 685 AppName: "newapp", 686 AppProject: "default", 687 }) 688 require.NoError(t, err) 689 assert.Equal(t, expectedResp, *resp) 690 }) 691 t.Run("Test_RepoNotPermitted", func(t *testing.T) { 692 repoServerClient := mocks.RepoServerServiceClient{} 693 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 694 enforcer := newEnforcer(kubeclientset) 695 696 url := "https://test" 697 db := &dbmocks.ArgoDB{} 698 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 699 db.On("GetProjectRepositories", "default").Return(nil, nil) 700 db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil) 701 expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"} 702 repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil) 703 appLister, projLister := newAppAndProjLister(defaultProjNoSources) 704 705 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 706 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 707 Source: &appsv1.ApplicationSource{ 708 RepoURL: url, 709 }, 710 AppName: "newapp", 711 AppProject: "default", 712 }) 713 require.Error(t, err, "repository 'https://test' not permitted in project 'default'") 714 assert.Nil(t, resp) 715 }) 716 t.Run("Test_ExistingApp", func(t *testing.T) { 717 repoServerClient := mocks.RepoServerServiceClient{} 718 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 719 enforcer := newEnforcer(kubeclientset) 720 721 url := "https://test" 722 db := &dbmocks.ArgoDB{} 723 db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(nil, nil) 724 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 725 db.On("GetProjectRepositories", "default").Return(nil, nil) 726 db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil) 727 expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"} 728 repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil) 729 appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp) 730 731 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 732 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 733 Source: guestbookApp.Spec.GetSourcePtrByIndex(0), 734 AppName: "guestbook", 735 AppProject: "default", 736 }) 737 require.NoError(t, err) 738 assert.Equal(t, expectedResp, *resp) 739 }) 740 t.Run("Test_ExistingMultiSourceApp001", func(t *testing.T) { 741 repoServerClient := mocks.RepoServerServiceClient{} 742 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 743 enforcer := newEnforcer(kubeclientset) 744 745 url := "https://helm.elastic.co" 746 helmRepos := []*appsv1.Repository{{Repo: url}, {Repo: url}} 747 db := &dbmocks.ArgoDB{} 748 db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(helmRepos, nil) 749 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 750 db.On("GetProjectRepositories", "default").Return(nil, nil) 751 db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil) 752 expectedResp := apiclient.RepoAppDetailsResponse{Type: "Helm"} 753 repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil) 754 appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp001) 755 756 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 757 sources := multiSourceApp001.Spec.GetSources() 758 assert.Len(t, sources, 2) 759 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 760 Source: &sources[0], 761 AppName: multiSourceApp001AppName, 762 AppProject: "default", 763 }) 764 require.NoError(t, err) 765 assert.Equal(t, expectedResp, *resp) 766 assert.Equal(t, "Helm", resp.Type) 767 // Next source 768 resp, err = s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 769 Source: &sources[1], 770 AppName: multiSourceApp001AppName, 771 AppProject: "default", 772 }) 773 require.NoError(t, err) 774 assert.Equal(t, expectedResp, *resp) 775 assert.Equal(t, "Helm", resp.Type) 776 }) 777 t.Run("Test_ExistingMultiSourceApp002", func(t *testing.T) { 778 repoServerClient := mocks.RepoServerServiceClient{} 779 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 780 enforcer := newEnforcer(kubeclientset) 781 782 url0 := "https://github.com/argoproj/argocd-example-apps.git" 783 url1 := "https://helm.elastic.co" 784 helmRepos := []*appsv1.Repository{{Repo: url0}, {Repo: url1}} 785 db := &dbmocks.ArgoDB{} 786 db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(helmRepos, nil) 787 db.On("GetRepository", t.Context(), url0, "default").Return(&appsv1.Repository{Repo: url0}, nil) 788 db.On("GetRepository", t.Context(), url1, "default").Return(&appsv1.Repository{Repo: url1}, nil) 789 db.On("GetProjectRepositories", "default").Return(nil, nil) 790 db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil) 791 expectedResp0 := apiclient.RepoAppDetailsResponse{Type: "Plugin"} 792 expectedResp1 := apiclient.RepoAppDetailsResponse{Type: "Helm"} 793 repoServerClient.On("GetAppDetails", t.Context(), mock.MatchedBy(func(req *apiclient.RepoServerAppDetailsQuery) bool { return req.Source.RepoURL == url0 })).Return(&expectedResp0, nil) 794 repoServerClient.On("GetAppDetails", t.Context(), mock.MatchedBy(func(req *apiclient.RepoServerAppDetailsQuery) bool { return req.Source.RepoURL == url1 })).Return(&expectedResp1, nil) 795 appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp002) 796 797 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 798 sources := multiSourceApp002.Spec.GetSources() 799 assert.Len(t, sources, 2) 800 801 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 802 Source: &sources[0], 803 AppName: multiSourceApp002AppName, 804 AppProject: "default", 805 }) 806 require.NoError(t, err) 807 assert.Equal(t, "Plugin", resp.Type) 808 assert.Equal(t, expectedResp0, *resp) 809 // Next source 810 resp, err = s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 811 Source: &sources[1], 812 AppName: multiSourceApp002AppName, 813 AppProject: "default", 814 }) 815 require.NoError(t, err) 816 assert.Equal(t, expectedResp1, *resp) 817 assert.Equal(t, "Helm", resp.Type) 818 }) 819 t.Run("Test_ExistingAppMismatchedProjectName", func(t *testing.T) { 820 repoServerClient := mocks.RepoServerServiceClient{} 821 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 822 enforcer := newEnforcer(kubeclientset) 823 824 url := "https://test" 825 db := &dbmocks.ArgoDB{} 826 db.On("GetRepository", t.Context(), url, "mismatch").Return(&appsv1.Repository{Repo: url}, nil) 827 appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp) 828 829 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 830 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 831 Source: guestbookApp.Spec.GetSourcePtrByIndex(0), 832 AppName: "guestbook", 833 AppProject: "mismatch", 834 }) 835 assert.Equal(t, err, common.PermissionDeniedAPIError) 836 assert.Nil(t, resp) 837 }) 838 t.Run("Test_ExistingAppSourceNotInHistory", func(t *testing.T) { 839 repoServerClient := mocks.RepoServerServiceClient{} 840 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 841 enforcer := newEnforcer(kubeclientset) 842 843 url := "https://test" 844 db := &dbmocks.ArgoDB{} 845 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 846 appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp) 847 differentSource := guestbookApp.Spec.Source.DeepCopy() 848 differentSource.Helm.ValueFiles = []string{"/etc/passwd"} 849 850 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 851 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 852 Source: differentSource, 853 AppName: "guestbook", 854 AppProject: "default", 855 }) 856 assert.Equal(t, err, common.PermissionDeniedAPIError) 857 assert.Nil(t, resp) 858 }) 859 t.Run("Test_ExistingAppSourceInHistory", func(t *testing.T) { 860 repoServerClient := mocks.RepoServerServiceClient{} 861 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 862 enforcer := newEnforcer(kubeclientset) 863 864 url := "https://test" 865 db := &dbmocks.ArgoDB{} 866 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 867 db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(nil, nil) 868 db.On("GetProjectRepositories", "default").Return(nil, nil) 869 db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil) 870 expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"} 871 repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil) 872 appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp) 873 previousSource := guestbookApp.Status.History[0].Source.DeepCopy() 874 previousSource.TargetRevision = guestbookApp.Status.History[0].Revision 875 876 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 877 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 878 Source: previousSource, 879 AppName: "guestbook", 880 AppProject: "default", 881 }) 882 require.NoError(t, err) 883 assert.Equal(t, expectedResp, *resp) 884 }) 885 886 t.Run("Test_ExistingAppMultiSourceNotInHistory", func(t *testing.T) { 887 repoServerClient := mocks.RepoServerServiceClient{} 888 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 889 enforcer := newEnforcer(kubeclientset) 890 891 url := "https://helm.elastic.co" 892 helmRepos := []*appsv1.Repository{{Repo: url}, {Repo: url}} 893 db := &dbmocks.ArgoDB{} 894 db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(helmRepos, nil) 895 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 896 db.On("GetProjectRepositories", "default").Return(nil, nil) 897 db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil) 898 expectedResp := apiclient.RepoAppDetailsResponse{Type: "Helm"} 899 repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil) 900 appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp001) 901 902 differentSource := multiSourceApp001.Spec.Sources[0].DeepCopy() 903 differentSource.Helm.ValueFiles = []string{"/etc/passwd"} 904 905 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 906 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 907 Source: differentSource, 908 AppName: multiSourceApp001AppName, 909 AppProject: "default", 910 SourceIndex: 0, 911 VersionId: 1, 912 }) 913 assert.Equal(t, err, common.PermissionDeniedAPIError) 914 assert.Nil(t, resp) 915 }) 916 t.Run("Test_ExistingAppMultiSourceInHistory", func(t *testing.T) { 917 repoServerClient := mocks.RepoServerServiceClient{} 918 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 919 enforcer := newEnforcer(kubeclientset) 920 921 url := "https://helm.elastic.co" 922 db := &dbmocks.ArgoDB{} 923 db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil) 924 db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(nil, nil) 925 db.On("GetProjectRepositories", "default").Return(nil, nil) 926 db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil) 927 expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"} 928 repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil) 929 appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp001) 930 previousSource := multiSourceApp001.Status.History[0].Sources[0].DeepCopy() 931 previousSource.TargetRevision = multiSourceApp001.Status.History[0].Revisions[0] 932 933 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 934 resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{ 935 Source: previousSource, 936 AppName: multiSourceApp001AppName, 937 AppProject: "default", 938 SourceIndex: 0, 939 VersionId: 1, 940 }) 941 require.NoError(t, err) 942 assert.Equal(t, expectedResp, *resp) 943 }) 944 } 945 946 type fixtures struct { 947 *cache.Cache 948 } 949 950 func newFixtures() *fixtures { 951 return &fixtures{cache.NewCache( 952 appstatecache.NewCache( 953 cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Hour)), 954 1*time.Minute, 955 ), 956 1*time.Minute, 957 1*time.Minute, 958 )} 959 } 960 961 func newEnforcer(kubeclientset *fake.Clientset) *rbac.Enforcer { 962 enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil) 963 _ = enforcer.SetBuiltinPolicy(assets.BuiltinPolicyCSV) 964 enforcer.SetDefaultRole("role:admin") 965 enforcer.SetClaimsEnforcerFunc(func(_ jwt.Claims, _ ...any) bool { 966 return true 967 }) 968 return enforcer 969 } 970 971 func TestGetRepository(t *testing.T) { 972 type args struct { 973 ctx context.Context 974 listRepositories func(context.Context, *repository.RepoQuery) (*appsv1.RepositoryList, error) 975 q *repository.RepoQuery 976 } 977 tests := []struct { 978 name string 979 args args 980 want *appsv1.Repository 981 error error 982 }{ 983 { 984 name: "empty project and no repos", 985 args: args{ 986 ctx: t.Context(), 987 listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) { 988 return &appsv1.RepositoryList{ 989 Items: []*appsv1.Repository{ 990 {Repo: "something-else"}, 991 }, 992 }, nil 993 }, 994 q: &repository.RepoQuery{}, 995 }, 996 want: nil, 997 error: common.PermissionDeniedAPIError, 998 }, 999 { 1000 name: "empty project and no matching repos", 1001 args: args{ 1002 ctx: t.Context(), 1003 listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) { 1004 return &appsv1.RepositoryList{}, nil 1005 }, 1006 q: &repository.RepoQuery{ 1007 Repo: "foobar", 1008 }, 1009 }, 1010 want: nil, 1011 error: common.PermissionDeniedAPIError, 1012 }, 1013 { 1014 name: "empty project + matching repo with an empty project", 1015 args: args{ 1016 ctx: t.Context(), 1017 listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) { 1018 return &appsv1.RepositoryList{ 1019 Items: []*appsv1.Repository{ 1020 {Repo: "foobar", Project: ""}, 1021 }, 1022 }, nil 1023 }, 1024 q: &repository.RepoQuery{ 1025 Repo: "foobar", 1026 AppProject: "", 1027 }, 1028 }, 1029 want: &appsv1.Repository{ 1030 Repo: "foobar", 1031 Project: "", 1032 }, 1033 error: nil, 1034 }, 1035 { 1036 name: "empty project + matching repo with a non-empty project", 1037 args: args{ 1038 ctx: t.Context(), 1039 listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) { 1040 return &appsv1.RepositoryList{ 1041 Items: []*appsv1.Repository{ 1042 {Repo: "foobar", Project: "foobar"}, 1043 }, 1044 }, nil 1045 }, 1046 q: &repository.RepoQuery{ 1047 Repo: "foobar", 1048 AppProject: "", 1049 }, 1050 }, 1051 want: &appsv1.Repository{ 1052 Repo: "foobar", 1053 Project: "foobar", 1054 }, 1055 error: nil, 1056 }, 1057 { 1058 name: "non-empty project + matching repo with an empty project", 1059 args: args{ 1060 ctx: t.Context(), 1061 listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) { 1062 return &appsv1.RepositoryList{ 1063 Items: []*appsv1.Repository{ 1064 {Repo: "foobar", Project: ""}, 1065 }, 1066 }, nil 1067 }, 1068 q: &repository.RepoQuery{ 1069 Repo: "foobar", 1070 AppProject: "foobar", 1071 }, 1072 }, 1073 want: nil, 1074 error: errors.New(`repository not found for url "foobar" and project "foobar"`), 1075 }, 1076 { 1077 name: "non-empty project + matching repo with a matching project", 1078 args: args{ 1079 ctx: t.Context(), 1080 listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) { 1081 return &appsv1.RepositoryList{ 1082 Items: []*appsv1.Repository{ 1083 {Repo: "foobar", Project: "foobar"}, 1084 }, 1085 }, nil 1086 }, 1087 q: &repository.RepoQuery{ 1088 Repo: "foobar", 1089 AppProject: "foobar", 1090 }, 1091 }, 1092 want: &appsv1.Repository{ 1093 Repo: "foobar", 1094 Project: "foobar", 1095 }, 1096 error: nil, 1097 }, 1098 { 1099 name: "non-empty project + matching repo with a non-matching project", 1100 args: args{ 1101 ctx: t.Context(), 1102 listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) { 1103 return &appsv1.RepositoryList{ 1104 Items: []*appsv1.Repository{ 1105 {Repo: "foobar", Project: "something-else"}, 1106 }, 1107 }, nil 1108 }, 1109 q: &repository.RepoQuery{ 1110 Repo: "foobar", 1111 AppProject: "foobar", 1112 }, 1113 }, 1114 want: nil, 1115 error: errors.New(`repository not found for url "foobar" and project "foobar"`), 1116 }, 1117 } 1118 for _, tt := range tests { 1119 t.Run(tt.name, func(t *testing.T) { 1120 got, err := getRepository(tt.args.ctx, tt.args.listRepositories, tt.args.q) 1121 assert.Equal(t, tt.error, err) 1122 assert.Equalf(t, tt.want, got, "getRepository(%v, %v) = %v", tt.args.ctx, tt.args.q, got) 1123 }) 1124 } 1125 } 1126 1127 func TestDeleteRepository(t *testing.T) { 1128 repositories := map[string]string{ 1129 "valid": "https://bitbucket.org/workspace/repo.git", 1130 // Check a wrongly formatter repo as well, see https://github.com/argoproj/argo-cd/issues/20921 1131 "invalid": "git clone https://bitbucket.org/workspace/repo.git", 1132 } 1133 1134 kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret) 1135 settingsMgr := settings.NewSettingsManager(t.Context(), kubeclientset, testNamespace) 1136 1137 for name, repo := range repositories { 1138 t.Run(name, func(t *testing.T) { 1139 repoServerClient := mocks.RepoServerServiceClient{} 1140 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 1141 1142 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 1143 enforcer := newEnforcer(kubeclientset) 1144 1145 db := &dbmocks.ArgoDB{} 1146 db.On("DeleteRepository", t.Context(), repo, "default").Return(nil) 1147 db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: repo, Project: "default"}}, nil) 1148 db.On("GetRepository", t.Context(), repo, "default").Return(&appsv1.Repository{Repo: repo, Project: "default"}, nil) 1149 appLister, projLister := newAppAndProjLister(defaultProj) 1150 1151 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false) 1152 resp, err := s.DeleteRepository(t.Context(), &repository.RepoQuery{Repo: repo, AppProject: "default"}) 1153 require.NoError(t, err) 1154 assert.Equal(t, repository.RepoResponse{}, *resp) 1155 }) 1156 } 1157 }