From c0e679a66c26543601909898a71637f04c36a973 Mon Sep 17 00:00:00 2001 From: Keith Chong Date: Fri, 16 Feb 2024 15:20:08 -0500 Subject: [PATCH] fix: Permission Denied error when calling GetAppDetails API (#17221) (#17229) Signed-off-by: Keith Chong --- server/repository/repository.go | 6 + server/repository/repository_test.go | 169 +++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) diff --git a/server/repository/repository.go b/server/repository/repository.go index 7787228ceb052..417a41ee306ef 100644 --- a/server/repository/repository.go +++ b/server/repository/repository.go @@ -564,6 +564,12 @@ func isSourceInHistory(app *v1alpha1.Application, source v1alpha1.ApplicationSou if source.Equals(&appSource) { return true } + appSources := app.Spec.GetSources() + for _, s := range appSources { + if source.Equals(&s) { + return true + } + } // Iterate history. When comparing items in our history, use the actual synced revision to // compare with the supplied source.targetRevision in the request. This is because // history[].source.targetRevision is ambiguous (e.g. HEAD), whereas diff --git a/server/repository/repository_test.go b/server/repository/repository_test.go index 11667319e57a0..9c294b5a332b9 100644 --- a/server/repository/repository_test.go +++ b/server/repository/repository_test.go @@ -135,6 +135,96 @@ var ( }, }, } + multiSourceApp001AppName = "msa-two-helm-types" + multiSourceApp001 = &appsv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: multiSourceApp001AppName, + Namespace: testNamespace, + }, + Spec: appsv1.ApplicationSpec{ + Project: "default", + Sources: []appsv1.ApplicationSource{ + { + RepoURL: "https://helm.elastic.co", + TargetRevision: "7.7.0", + Chart: "elasticsearch", + Helm: &appsv1.ApplicationSourceHelm{ + ValueFiles: []string{"values.yaml"}, + }, + }, + { + RepoURL: "https://helm.elastic.co", + TargetRevision: "7.6.0", + Chart: "elasticsearch", + Helm: &appsv1.ApplicationSourceHelm{ + ValueFiles: []string{"values.yaml"}, + }, + }, + }, + }, + Status: appsv1.ApplicationStatus{ + History: appsv1.RevisionHistories{ + { + Revision: "HEAD", + Sources: []appsv1.ApplicationSource{ + { + RepoURL: "https://helm.elastic.co", + TargetRevision: "7.6.0", + Helm: &appsv1.ApplicationSourceHelm{ + ValueFiles: []string{"values-old.yaml"}, + }, + }, + }, + }, + }, + }, + } + multiSourceApp002AppName = "msa-one-plugin-one-helm" + multiSourceApp002 = &appsv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: multiSourceApp002AppName, + Namespace: testNamespace, + }, + Spec: appsv1.ApplicationSpec{ + Project: "default", + Sources: []appsv1.ApplicationSource{ + { + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + Path: "sock-shop", + TargetRevision: "HEAD", + }, + { + RepoURL: "https://helm.elastic.co", + TargetRevision: "7.7.0", + Chart: "elasticsearch", + Helm: &appsv1.ApplicationSourceHelm{ + ValueFiles: []string{"values.yaml"}, + }, + }, + }, + }, + Status: appsv1.ApplicationStatus{ + History: appsv1.RevisionHistories{ + { + Revision: "HEAD", + Sources: []appsv1.ApplicationSource{ + { + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "1.0.0", + }, + }, + }, + }, + }, + } ) func newAppAndProjLister(objects ...runtime.Object) (applisters.ApplicationLister, k8scache.SharedIndexInformer) { @@ -571,6 +661,85 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expectedResp, *resp) }) + t.Run("Test_ExistingMultiSourceApp001", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + enforcer := newEnforcer(kubeclientset) + + url := "https://helm.elastic.co" + helmRepos := []*appsv1.Repository{{Repo: url}, {Repo: url}} + db := &dbmocks.ArgoDB{} + db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(helmRepos, nil) + db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil) + db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) + db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) + expectedResp := apiclient.RepoAppDetailsResponse{Type: "Helm"} + repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil) + appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp001) + + s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) + sources := multiSourceApp001.Spec.GetSources() + assert.Equal(t, 2, len(sources)) + resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ + Source: &sources[0], + AppName: multiSourceApp001AppName, + AppProject: "default", + }) + assert.NoError(t, err) + assert.Equal(t, expectedResp, *resp) + assert.Equal(t, "Helm", resp.Type) + // Next source + resp, err = s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ + Source: &sources[1], + AppName: multiSourceApp001AppName, + AppProject: "default", + }) + assert.NoError(t, err) + assert.Equal(t, expectedResp, *resp) + assert.Equal(t, "Helm", resp.Type) + }) + t.Run("Test_ExistingMultiSourceApp002", func(t *testing.T) { + repoServerClient := mocks.RepoServerServiceClient{} + repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient} + enforcer := newEnforcer(kubeclientset) + + url0 := "https://github.com/argoproj/argocd-example-apps.git" + url1 := "https://helm.elastic.co" + helmRepos := []*appsv1.Repository{{Repo: url0}, {Repo: url1}} + db := &dbmocks.ArgoDB{} + db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(helmRepos, nil) + db.On("GetRepository", context.TODO(), url0).Return(&appsv1.Repository{Repo: url0}, nil) + db.On("GetRepository", context.TODO(), url1).Return(&appsv1.Repository{Repo: url1}, nil) + db.On("GetProjectRepositories", context.TODO(), "default").Return(nil, nil) + db.On("GetProjectClusters", context.TODO(), "default").Return(nil, nil) + expectedResp0 := apiclient.RepoAppDetailsResponse{Type: "Plugin"} + expectedResp1 := apiclient.RepoAppDetailsResponse{Type: "Helm"} + repoServerClient.On("GetAppDetails", context.TODO(), mock.MatchedBy(func(req *apiclient.RepoServerAppDetailsQuery) bool { return req.Source.RepoURL == url0 })).Return(&expectedResp0, nil) + repoServerClient.On("GetAppDetails", context.TODO(), mock.MatchedBy(func(req *apiclient.RepoServerAppDetailsQuery) bool { return req.Source.RepoURL == url1 })).Return(&expectedResp1, nil) + appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp002) + + s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr) + sources := multiSourceApp002.Spec.GetSources() + assert.Equal(t, 2, len(sources)) + + resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ + Source: &sources[0], + AppName: multiSourceApp002AppName, + AppProject: "default", + }) + assert.NoError(t, err) + assert.Equal(t, "Plugin", resp.Type) + assert.Equal(t, expectedResp0, *resp) + // Next source + resp, err = s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{ + Source: &sources[1], + AppName: multiSourceApp002AppName, + AppProject: "default", + }) + assert.NoError(t, err) + assert.Equal(t, expectedResp1, *resp) + assert.Equal(t, "Helm", resp.Type) + }) t.Run("Test_ExistingAppMismatchedProjectName", func(t *testing.T) { repoServerClient := mocks.RepoServerServiceClient{} repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}