github.com/argoproj/argo-cd/v2@v2.10.9/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/v4" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/mock" 12 "google.golang.org/grpc/codes" 13 "google.golang.org/grpc/status" 14 corev1 "k8s.io/api/core/v1" 15 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 v1 "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/v2/common" 22 "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository" 23 appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 24 fakeapps "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake" 25 appinformer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions" 26 applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" 27 "github.com/argoproj/argo-cd/v2/reposerver/apiclient" 28 "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks" 29 "github.com/argoproj/argo-cd/v2/server/cache" 30 "github.com/argoproj/argo-cd/v2/util/assets" 31 cacheutil "github.com/argoproj/argo-cd/v2/util/cache" 32 appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" 33 "github.com/argoproj/argo-cd/v2/util/db" 34 dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" 35 "github.com/argoproj/argo-cd/v2/util/rbac" 36 "github.com/argoproj/argo-cd/v2/util/settings" 37 38 "github.com/argoproj/argo-cd/v2/pkg/apis/application" 39 ) 40 41 const testNamespace = "default" 42 43 var ( 44 argocdCM = corev1.ConfigMap{ 45 ObjectMeta: v1.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: v1.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 ) 139 140 func newAppAndProjLister(objects ...runtime.Object) (applisters.ApplicationLister, k8scache.SharedIndexInformer) { 141 fakeAppsClientset := fakeapps.NewSimpleClientset(objects...) 142 factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(""), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) 143 projInformer := factory.Argoproj().V1alpha1().AppProjects() 144 appsInformer := factory.Argoproj().V1alpha1().Applications() 145 for _, obj := range objects { 146 switch obj.(type) { 147 case *appsv1.AppProject: 148 _ = projInformer.Informer().GetStore().Add(obj) 149 case *appsv1.Application: 150 _ = appsInformer.Informer().GetStore().Add(obj) 151 } 152 } 153 appLister := appsInformer.Lister() 154 return appLister, projInformer.Informer() 155 } 156 157 func Test_createRBACObject(t *testing.T) { 158 object := createRBACObject("test-prj", "test-repo") 159 assert.Equal(t, "test-prj/test-repo", object) 160 objectWithoutPrj := createRBACObject("", "test-repo") 161 assert.Equal(t, "test-repo", objectWithoutPrj) 162 } 163 164 func TestRepositoryServer(t *testing.T) { 165 kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret) 166 settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace) 167 enforcer := newEnforcer(kubeclientset) 168 appLister, projInformer := newAppAndProjLister(defaultProj) 169 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 170 171 t.Run("Test_getRepo", func(t *testing.T) { 172 repoServerClient := mocks.RepoServerServiceClient{} 173 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 174 175 s := NewServer(&repoServerClientset, argoDB, enforcer, nil, appLister, projInformer, testNamespace, settingsMgr) 176 url := "https://test" 177 repo, _ := s.getRepo(context.TODO(), url) 178 assert.Equal(t, repo.Repo, url) 179 }) 180 181 t.Run("Test_validateAccess", func(t *testing.T) { 182 repoServerClient := mocks.RepoServerServiceClient{} 183 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 184 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 185 186 s := NewServer(&repoServerClientset, argoDB, enforcer, nil, appLister, projInformer, testNamespace, settingsMgr) 187 url := "https://test" 188 _, err := s.ValidateAccess(context.TODO(), &repository.RepoAccessQuery{ 189 Repo: url, 190 }) 191 assert.Nil(t, err) 192 }) 193 194 t.Run("Test_Get", func(t *testing.T) { 195 repoServerClient := mocks.RepoServerServiceClient{} 196 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 197 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 198 199 url := "https://test" 200 db := &dbmocks.ArgoDB{} 201 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 202 db.On("RepositoryExists", context.TODO(), url).Return(true, nil) 203 204 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr) 205 repo, err := s.Get(context.TODO(), &repository.RepoQuery{ 206 Repo: url, 207 }) 208 assert.Nil(t, err) 209 assert.Equal(t, repo.Repo, url) 210 }) 211 212 t.Run("Test_GetInherited", func(t *testing.T) { 213 repoServerClient := mocks.RepoServerServiceClient{} 214 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 215 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 216 217 url := "https://test" 218 db := &dbmocks.ArgoDB{} 219 testRepo := &appsv1.Repository{ 220 Repo: url, 221 Type: "git", 222 Username: "foo", 223 InheritedCreds: true, 224 } 225 db.On("GetRepository", context.TODO(), url).Return(testRepo, nil) 226 db.On("RepositoryExists", context.TODO(), url).Return(true, nil) 227 228 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr) 229 repo, err := s.Get(context.TODO(), &repository.RepoQuery{ 230 Repo: url, 231 }) 232 assert.Nil(t, err) 233 234 testRepo.ConnectionState = repo.ConnectionState // overwrite connection state on our test object to simplify comparison below 235 236 assert.Equal(t, testRepo, repo) 237 }) 238 239 t.Run("Test_GetWithErrorShouldReturn403", func(t *testing.T) { 240 repoServerClient := mocks.RepoServerServiceClient{} 241 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 242 243 url := "https://test" 244 db := &dbmocks.ArgoDB{} 245 db.On("GetRepository", context.TODO(), url).Return(nil, errors.New("some error")) 246 db.On("RepositoryExists", context.TODO(), url).Return(true, nil) 247 248 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr) 249 repo, err := s.Get(context.TODO(), &repository.RepoQuery{ 250 Repo: url, 251 }) 252 assert.Nil(t, repo) 253 assert.Equal(t, err, errPermissionDenied) 254 }) 255 256 t.Run("Test_GetWithNotExistRepoShouldReturn404", func(t *testing.T) { 257 repoServerClient := mocks.RepoServerServiceClient{} 258 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 259 260 url := "https://test" 261 db := &dbmocks.ArgoDB{} 262 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 263 db.On("RepositoryExists", context.TODO(), url).Return(false, nil) 264 265 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr) 266 repo, err := s.Get(context.TODO(), &repository.RepoQuery{ 267 Repo: url, 268 }) 269 assert.Nil(t, repo) 270 assert.Equal(t, "rpc error: code = NotFound desc = repo 'https://test' not found", err.Error()) 271 }) 272 273 t.Run("Test_CreateRepositoryWithoutUpsert", func(t *testing.T) { 274 repoServerClient := mocks.RepoServerServiceClient{} 275 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 276 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 277 278 db := &dbmocks.ArgoDB{} 279 db.On("GetRepository", context.TODO(), "test").Return(nil, errors.New("not found")) 280 db.On("CreateRepository", context.TODO(), mock.Anything).Return(&apiclient.TestRepositoryResponse{}).Return(&appsv1.Repository{ 281 Repo: "repo", 282 Project: "proj", 283 }, nil) 284 285 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr) 286 repo, err := s.CreateRepository(context.TODO(), &repository.RepoCreateRequest{ 287 Repo: &appsv1.Repository{ 288 Repo: "test", 289 Username: "test", 290 }, 291 }) 292 assert.Nil(t, err) 293 assert.Equal(t, repo.Repo, "repo") 294 }) 295 296 t.Run("Test_CreateRepositoryWithUpsert", func(t *testing.T) { 297 repoServerClient := mocks.RepoServerServiceClient{} 298 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 299 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 300 301 db := &dbmocks.ArgoDB{} 302 db.On("GetRepository", context.TODO(), "test").Return(&appsv1.Repository{ 303 Repo: "test", 304 Username: "test", 305 }, nil) 306 db.On("CreateRepository", context.TODO(), mock.Anything).Return(nil, status.Errorf(codes.AlreadyExists, "repository already exists")) 307 db.On("UpdateRepository", context.TODO(), mock.Anything).Return(nil, nil) 308 309 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr) 310 repo, err := s.CreateRepository(context.TODO(), &repository.RepoCreateRequest{ 311 Repo: &appsv1.Repository{ 312 Repo: "test", 313 Username: "test", 314 }, 315 Upsert: true, 316 }) 317 318 assert.Nil(t, err) 319 assert.Equal(t, repo.Repo, "test") 320 }) 321 322 t.Run("Test_ListRepositories", func(t *testing.T) { 323 repoServerClient := mocks.RepoServerServiceClient{} 324 repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil) 325 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 326 enforcer := newEnforcer(kubeclientset) 327 328 url := "https://test" 329 db := &dbmocks.ArgoDB{} 330 db.On("GetRepository", context.TODO(), url).Return(nil, nil) 331 db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil) 332 db.On("ListRepositories", context.TODO()).Return([]*appsv1.Repository{&fakeRepo, &fakeRepo}, nil) 333 334 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr) 335 resp, err := s.ListRepositories(context.TODO(), &repository.RepoQuery{}) 336 assert.NoError(t, err) 337 assert.Equal(t, 2, len(resp.Items)) 338 }) 339 } 340 341 func TestRepositoryServerListApps(t *testing.T) { 342 kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret) 343 settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace) 344 345 t.Run("Test_WithoutAppCreateUpdatePrivileges", func(t *testing.T) { 346 repoServerClient := mocks.RepoServerServiceClient{} 347 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 348 enforcer := newEnforcer(kubeclientset) 349 enforcer.SetDefaultRole("role:readonly") 350 351 url := "https://test" 352 db := &dbmocks.ArgoDB{} 353 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 354 appLister, projLister := newAppAndProjLister(defaultProj) 355 356 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 357 resp, err := s.ListApps(context.TODO(), &repository.RepoAppsQuery{ 358 Repo: "https://test", 359 Revision: "HEAD", 360 AppName: "foo", 361 AppProject: "default", 362 }) 363 assert.Nil(t, resp) 364 assert.Equal(t, err, errPermissionDenied) 365 }) 366 367 t.Run("Test_WithAppCreateUpdatePrivileges", func(t *testing.T) { 368 repoServerClient := mocks.RepoServerServiceClient{} 369 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 370 enforcer := newEnforcer(kubeclientset) 371 enforcer.SetDefaultRole("role:admin") 372 appLister, projLister := newAppAndProjLister(defaultProj) 373 374 url := "https://test" 375 db := &dbmocks.ArgoDB{} 376 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 377 db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) 378 db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) 379 repoServerClient.On("ListApps", context.TODO(), mock.Anything).Return(&apiclient.AppList{ 380 Apps: map[string]string{ 381 "path/to/dir": "Kustomize", 382 }, 383 }, nil) 384 385 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 386 resp, err := s.ListApps(context.TODO(), &repository.RepoAppsQuery{ 387 Repo: "https://test", 388 Revision: "HEAD", 389 AppName: "foo", 390 AppProject: "default", 391 }) 392 assert.NoError(t, err) 393 assert.Len(t, resp.Items, 1) 394 assert.Equal(t, "path/to/dir", resp.Items[0].Path) 395 assert.Equal(t, "Kustomize", resp.Items[0].Type) 396 }) 397 398 t.Run("Test_WithAppCreateUpdatePrivilegesRepoNotAllowed", func(t *testing.T) { 399 repoServerClient := mocks.RepoServerServiceClient{} 400 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 401 enforcer := newEnforcer(kubeclientset) 402 enforcer.SetDefaultRole("role:admin") 403 appLister, projLister := newAppAndProjLister(defaultProjNoSources) 404 405 url := "https://test" 406 db := &dbmocks.ArgoDB{} 407 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 408 db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) 409 db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) 410 repoServerClient.On("ListApps", context.TODO(), mock.Anything).Return(&apiclient.AppList{ 411 Apps: map[string]string{ 412 "path/to/dir": "Kustomize", 413 }, 414 }, nil) 415 416 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 417 resp, err := s.ListApps(context.TODO(), &repository.RepoAppsQuery{ 418 Repo: "https://test", 419 Revision: "HEAD", 420 AppName: "foo", 421 AppProject: "default", 422 }) 423 assert.Nil(t, resp) 424 assert.Error(t, err, "repository 'https://test' not permitted in project 'default'") 425 }) 426 } 427 428 func TestRepositoryServerGetAppDetails(t *testing.T) { 429 kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret) 430 settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace) 431 432 t.Run("Test_WithoutRepoReadPrivileges", func(t *testing.T) { 433 repoServerClient := mocks.RepoServerServiceClient{} 434 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 435 enforcer := newEnforcer(kubeclientset) 436 enforcer.SetDefaultRole("") 437 438 url := "https://test" 439 db := &dbmocks.ArgoDB{} 440 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 441 appLister, projLister := newAppAndProjLister(defaultProj) 442 443 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 444 resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ 445 Source: &appsv1.ApplicationSource{ 446 RepoURL: url, 447 }, 448 AppName: "newapp", 449 AppProject: "default", 450 }) 451 assert.Nil(t, resp) 452 assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: repositories, get, https://test") 453 }) 454 t.Run("Test_WithoutAppReadPrivileges", func(t *testing.T) { 455 repoServerClient := mocks.RepoServerServiceClient{} 456 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 457 enforcer := newEnforcer(kubeclientset) 458 _ = enforcer.SetUserPolicy("p, role:readrepos, repositories, get, *, allow") 459 enforcer.SetDefaultRole("role:readrepos") 460 461 url := "https://test" 462 db := &dbmocks.ArgoDB{} 463 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 464 appLister, projLister := newAppAndProjLister(defaultProj) 465 466 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 467 resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ 468 Source: &appsv1.ApplicationSource{ 469 RepoURL: url, 470 }, 471 AppName: "newapp", 472 AppProject: "default", 473 }) 474 assert.Nil(t, resp) 475 assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, get, default/newapp") 476 }) 477 t.Run("Test_WithoutCreatePrivileges", func(t *testing.T) { 478 repoServerClient := mocks.RepoServerServiceClient{} 479 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 480 enforcer := newEnforcer(kubeclientset) 481 enforcer.SetDefaultRole("role:readonly") 482 483 url := "https://test" 484 db := &dbmocks.ArgoDB{} 485 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 486 appLister, projLister := newAppAndProjLister(defaultProj) 487 488 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 489 resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ 490 Source: &appsv1.ApplicationSource{ 491 RepoURL: url, 492 }, 493 AppName: "newapp", 494 AppProject: "default", 495 }) 496 assert.Nil(t, resp) 497 assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, create, default/newapp") 498 }) 499 t.Run("Test_WithCreatePrivileges", func(t *testing.T) { 500 repoServerClient := mocks.RepoServerServiceClient{} 501 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 502 enforcer := newEnforcer(kubeclientset) 503 504 url := "https://test" 505 db := &dbmocks.ArgoDB{} 506 db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil) 507 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 508 db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) 509 db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) 510 expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"} 511 repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil) 512 appLister, projLister := newAppAndProjLister(defaultProj) 513 514 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 515 resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ 516 Source: &appsv1.ApplicationSource{ 517 RepoURL: url, 518 }, 519 AppName: "newapp", 520 AppProject: "default", 521 }) 522 assert.NoError(t, err) 523 assert.Equal(t, expectedResp, *resp) 524 }) 525 t.Run("Test_RepoNotPermitted", func(t *testing.T) { 526 repoServerClient := mocks.RepoServerServiceClient{} 527 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 528 enforcer := newEnforcer(kubeclientset) 529 530 url := "https://test" 531 db := &dbmocks.ArgoDB{} 532 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 533 db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) 534 db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) 535 expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"} 536 repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil) 537 appLister, projLister := newAppAndProjLister(defaultProjNoSources) 538 539 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 540 resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ 541 Source: &appsv1.ApplicationSource{ 542 RepoURL: url, 543 }, 544 AppName: "newapp", 545 AppProject: "default", 546 }) 547 assert.Error(t, err, "repository 'https://test' not permitted in project 'default'") 548 assert.Nil(t, resp) 549 }) 550 t.Run("Test_ExistingApp", func(t *testing.T) { 551 repoServerClient := mocks.RepoServerServiceClient{} 552 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 553 enforcer := newEnforcer(kubeclientset) 554 555 url := "https://test" 556 db := &dbmocks.ArgoDB{} 557 db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil) 558 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 559 db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) 560 db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) 561 expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"} 562 repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil) 563 appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp) 564 565 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 566 resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ 567 Source: guestbookApp.Spec.GetSourcePtr(), 568 AppName: "guestbook", 569 AppProject: "default", 570 }) 571 assert.NoError(t, err) 572 assert.Equal(t, expectedResp, *resp) 573 }) 574 t.Run("Test_ExistingAppMismatchedProjectName", func(t *testing.T) { 575 repoServerClient := mocks.RepoServerServiceClient{} 576 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 577 enforcer := newEnforcer(kubeclientset) 578 579 url := "https://test" 580 db := &dbmocks.ArgoDB{} 581 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 582 appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp) 583 584 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 585 resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ 586 Source: guestbookApp.Spec.GetSourcePtr(), 587 AppName: "guestbook", 588 AppProject: "mismatch", 589 }) 590 assert.Equal(t, errPermissionDenied, err) 591 assert.Nil(t, resp) 592 }) 593 t.Run("Test_ExistingAppSourceNotInHistory", func(t *testing.T) { 594 repoServerClient := mocks.RepoServerServiceClient{} 595 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 596 enforcer := newEnforcer(kubeclientset) 597 598 url := "https://test" 599 db := &dbmocks.ArgoDB{} 600 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 601 appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp) 602 differentSource := guestbookApp.Spec.Source.DeepCopy() 603 differentSource.Helm.ValueFiles = []string{"/etc/passwd"} 604 605 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 606 resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ 607 Source: differentSource, 608 AppName: "guestbook", 609 AppProject: "default", 610 }) 611 assert.Equal(t, errPermissionDenied, err) 612 assert.Nil(t, resp) 613 }) 614 t.Run("Test_ExistingAppSourceInHistory", func(t *testing.T) { 615 repoServerClient := mocks.RepoServerServiceClient{} 616 repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} 617 enforcer := newEnforcer(kubeclientset) 618 619 url := "https://test" 620 db := &dbmocks.ArgoDB{} 621 db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) 622 db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil) 623 db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) 624 db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) 625 expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"} 626 repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil) 627 appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp) 628 previousSource := guestbookApp.Status.History[0].Source.DeepCopy() 629 previousSource.TargetRevision = guestbookApp.Status.History[0].Revision 630 631 s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) 632 resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ 633 Source: previousSource, 634 AppName: "guestbook", 635 AppProject: "default", 636 }) 637 assert.NoError(t, err) 638 assert.Equal(t, expectedResp, *resp) 639 }) 640 } 641 642 type fixtures struct { 643 *cache.Cache 644 } 645 646 func newFixtures() *fixtures { 647 return &fixtures{cache.NewCache( 648 appstatecache.NewCache( 649 cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Hour)), 650 1*time.Minute, 651 ), 652 1*time.Minute, 653 1*time.Minute, 654 1*time.Minute, 655 )} 656 } 657 658 func newEnforcer(kubeclientset *fake.Clientset) *rbac.Enforcer { 659 enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil) 660 _ = enforcer.SetBuiltinPolicy(assets.BuiltinPolicyCSV) 661 enforcer.SetDefaultRole("role:admin") 662 enforcer.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool { 663 return true 664 }) 665 return enforcer 666 }