Skip to content

Commit 9a3235e

Browse files
fix(3.1): change the appset namespace to server namespace when generating appset (argoproj#24478)
Signed-off-by: nitishfy <[email protected]> Co-authored-by: Alexandre Gaudreault <[email protected]>
1 parent 3320f1e commit 9a3235e

File tree

4 files changed

+99
-21
lines changed

4 files changed

+99
-21
lines changed

applicationset/generators/git.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ type GitGenerator struct {
2929
}
3030

3131
// NewGitGenerator creates a new instance of Git Generator
32-
func NewGitGenerator(repos services.Repos, namespace string) Generator {
32+
func NewGitGenerator(repos services.Repos, controllerNamespace string) Generator {
3333
g := &GitGenerator{
3434
repos: repos,
35-
namespace: namespace,
35+
namespace: controllerNamespace,
3636
}
3737

3838
return g
@@ -78,11 +78,11 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
7878
if !strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
7979
project := appSet.Spec.Template.Spec.Project
8080
appProject := &argoprojiov1alpha1.AppProject{}
81-
namespace := g.namespace
82-
if namespace == "" {
83-
namespace = appSet.Namespace
81+
controllerNamespace := g.namespace
82+
if controllerNamespace == "" {
83+
controllerNamespace = appSet.Namespace
8484
}
85-
if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: namespace}, appProject); err != nil {
85+
if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: controllerNamespace}, appProject); err != nil {
8686
return nil, fmt.Errorf("error getting project %s: %w", project, err)
8787
}
8888
// we need to verify the signature on the Git revision if GPG is enabled

applicationset/generators/utils.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ import (
1010
"github.com/argoproj/argo-cd/v3/applicationset/services"
1111
)
1212

13-
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, namespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator {
13+
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, controllerNamespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator {
1414
terminalGenerators := map[string]Generator{
1515
"List": NewListGenerator(),
16-
"Clusters": NewClusterGenerator(ctx, c, k8sClient, namespace),
17-
"Git": NewGitGenerator(argoCDService, namespace),
16+
"Clusters": NewClusterGenerator(ctx, c, k8sClient, controllerNamespace),
17+
"Git": NewGitGenerator(argoCDService, controllerNamespace),
1818
"SCMProvider": NewSCMProviderGenerator(c, scmConfig),
19-
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace),
19+
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, controllerNamespace),
2020
"PullRequest": NewPullRequestGenerator(c, scmConfig),
21-
"Plugin": NewPluginGenerator(c, namespace),
21+
"Plugin": NewPluginGenerator(c, controllerNamespace),
2222
}
2323

2424
nestedGenerators := map[string]Generator{

server/applicationset/applicationset.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ func (s *Server) Create(ctx context.Context, q *applicationset.ApplicationSetCre
200200
}
201201

202202
if q.GetDryRun() {
203-
apps, err := s.generateApplicationSetApps(ctx, log.WithField("applicationset", appset.Name), *appset, namespace)
203+
apps, err := s.generateApplicationSetApps(ctx, log.WithField("applicationset", appset.Name), *appset)
204204
if err != nil {
205205
return nil, fmt.Errorf("unable to generate Applications of ApplicationSet: %w", err)
206206
}
@@ -260,12 +260,12 @@ func (s *Server) Create(ctx context.Context, q *applicationset.ApplicationSetCre
260260
return updated, nil
261261
}
262262

263-
func (s *Server) generateApplicationSetApps(ctx context.Context, logEntry *log.Entry, appset v1alpha1.ApplicationSet, namespace string) ([]v1alpha1.Application, error) {
263+
func (s *Server) generateApplicationSetApps(ctx context.Context, logEntry *log.Entry, appset v1alpha1.ApplicationSet) ([]v1alpha1.Application, error) {
264264
argoCDDB := s.db
265265

266266
scmConfig := generators.NewSCMConfig(s.ScmRootCAPath, s.AllowedScmProviders, s.EnableScmProviders, s.EnableGitHubAPIMetrics, github_app.NewAuthCredentials(argoCDDB.(db.RepoCredsDB)), true)
267267
argoCDService := services.NewArgoCDService(s.db, s.GitSubmoduleEnabled, s.repoClientSet, s.EnableNewGitFileGlobbing)
268-
appSetGenerators := generators.GetGenerators(ctx, s.client, s.k8sClient, namespace, argoCDService, s.dynamicClient, scmConfig)
268+
appSetGenerators := generators.GetGenerators(ctx, s.client, s.k8sClient, s.ns, argoCDService, s.dynamicClient, scmConfig)
269269

270270
apps, _, err := appsettemplate.GenerateApplications(logEntry, appset, appSetGenerators, &appsetutils.Render{}, s.client)
271271
if err != nil {
@@ -363,11 +363,15 @@ func (s *Server) Generate(ctx context.Context, q *applicationset.ApplicationSetG
363363
if appset == nil {
364364
return nil, errors.New("error creating ApplicationSets: ApplicationSets is nil in request")
365365
}
366-
namespace := s.appsetNamespaceOrDefault(appset.Namespace)
367366

367+
// The RBAC check needs to be performed against the appset namespace
368+
// However, when trying to generate params, the server namespace needs
369+
// to be passed.
370+
namespace := s.appsetNamespaceOrDefault(appset.Namespace)
368371
if !s.isNamespaceEnabled(namespace) {
369372
return nil, security.NamespaceNotPermittedError(namespace)
370373
}
374+
371375
projectName, err := s.validateAppSet(appset)
372376
if err != nil {
373377
return nil, fmt.Errorf("error validating ApplicationSets: %w", err)
@@ -380,7 +384,16 @@ func (s *Server) Generate(ctx context.Context, q *applicationset.ApplicationSetG
380384
logger := log.New()
381385
logger.SetOutput(logs)
382386

383-
apps, err := s.generateApplicationSetApps(ctx, logger.WithField("applicationset", appset.Name), *appset, namespace)
387+
// The server namespace will be used in the function
388+
// since this is the exact namespace that is being used
389+
// to generate parameters (especially for git generator).
390+
//
391+
// In case of Git generator, if the namespace is set to
392+
// appset namespace, we'll look for a project in the appset
393+
// namespace that would lead to error when generating params
394+
// for an appset in any namespace feature.
395+
// See https://github.com/argoproj/argo-cd/issues/22942
396+
apps, err := s.generateApplicationSetApps(ctx, logger.WithField("applicationset", appset.Name), *appset)
384397
if err != nil {
385398
return nil, fmt.Errorf("unable to generate Applications of ApplicationSet: %w\n%s", err, logs.String())
386399
}

server/applicationset/applicationset_test.go

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import (
44
"sort"
55
"testing"
66

7+
"sigs.k8s.io/controller-runtime/pkg/client"
8+
cr_fake "sigs.k8s.io/controller-runtime/pkg/client/fake"
9+
710
"github.com/argoproj/gitops-engine/pkg/health"
811
"github.com/argoproj/pkg/v2/sync"
912
"github.com/stretchr/testify/assert"
@@ -50,7 +53,7 @@ func fakeCluster() *appsv1.Cluster {
5053
}
5154

5255
// return an ApplicationServiceServer which returns fake data
53-
func newTestAppSetServer(t *testing.T, objects ...runtime.Object) *Server {
56+
func newTestAppSetServer(t *testing.T, objects ...client.Object) *Server {
5457
t.Helper()
5558
f := func(enf *rbac.Enforcer) {
5659
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
@@ -61,7 +64,7 @@ func newTestAppSetServer(t *testing.T, objects ...runtime.Object) *Server {
6164
}
6265

6366
// return an ApplicationServiceServer which returns fake data
64-
func newTestNamespacedAppSetServer(t *testing.T, objects ...runtime.Object) *Server {
67+
func newTestNamespacedAppSetServer(t *testing.T, objects ...client.Object) *Server {
6568
t.Helper()
6669
f := func(enf *rbac.Enforcer) {
6770
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
@@ -71,7 +74,7 @@ func newTestNamespacedAppSetServer(t *testing.T, objects ...runtime.Object) *Ser
7174
return newTestAppSetServerWithEnforcerConfigure(t, f, scopedNamespaces, objects...)
7275
}
7376

74-
func newTestAppSetServerWithEnforcerConfigure(t *testing.T, f func(*rbac.Enforcer), namespace string, objects ...runtime.Object) *Server {
77+
func newTestAppSetServerWithEnforcerConfigure(t *testing.T, f func(*rbac.Enforcer), namespace string, objects ...client.Object) *Server {
7578
t.Helper()
7679
kubeclientset := fake.NewClientset(&corev1.ConfigMap{
7780
ObjectMeta: metav1.ObjectMeta{
@@ -115,7 +118,11 @@ func newTestAppSetServerWithEnforcerConfigure(t *testing.T, f func(*rbac.Enforce
115118

116119
objects = append(objects, defaultProj, myProj)
117120

118-
fakeAppsClientset := apps.NewSimpleClientset(objects...)
121+
runtimeObjects := make([]runtime.Object, len(objects))
122+
for i := range objects {
123+
runtimeObjects[i] = objects[i]
124+
}
125+
fakeAppsClientset := apps.NewSimpleClientset(runtimeObjects...)
119126
factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(namespace), appinformer.WithTweakListOptions(func(_ *metav1.ListOptions) {}))
120127
fakeProjLister := factory.Argoproj().V1alpha1().AppProjects().Lister().AppProjects(testNamespace)
121128

@@ -138,6 +145,13 @@ func newTestAppSetServerWithEnforcerConfigure(t *testing.T, f func(*rbac.Enforce
138145
panic("Timed out waiting for caches to sync")
139146
}
140147

148+
scheme := runtime.NewScheme()
149+
err = appsv1.AddToScheme(scheme)
150+
require.NoError(t, err)
151+
err = corev1.AddToScheme(scheme)
152+
require.NoError(t, err)
153+
crClient := cr_fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
154+
141155
projInformer := factory.Argoproj().V1alpha1().AppProjects().Informer()
142156
go projInformer.Run(ctx.Done())
143157
if !k8scache.WaitForCacheSync(ctx.Done(), projInformer.HasSynced) {
@@ -148,7 +162,7 @@ func newTestAppSetServerWithEnforcerConfigure(t *testing.T, f func(*rbac.Enforce
148162
db,
149163
kubeclientset,
150164
nil,
151-
nil,
165+
crClient,
152166
enforcer,
153167
nil,
154168
fakeAppsClientset,
@@ -640,3 +654,54 @@ func TestResourceTree(t *testing.T) {
640654
assert.EqualError(t, err, "namespace 'NOT-ALLOWED' is not permitted")
641655
})
642656
}
657+
658+
func TestAppSet_Generate_Cluster(t *testing.T) {
659+
appSet1 := newTestAppSet(func(appset *appsv1.ApplicationSet) {
660+
appset.Name = "AppSet1"
661+
appset.Spec.Template.Name = "{{name}}"
662+
appset.Spec.Generators = []appsv1.ApplicationSetGenerator{
663+
{
664+
Clusters: &appsv1.ClusterGenerator{},
665+
},
666+
}
667+
})
668+
669+
t.Run("Generate in default namespace", func(t *testing.T) {
670+
appSetServer := newTestAppSetServer(t, appSet1)
671+
appsetQuery := applicationset.ApplicationSetGenerateRequest{
672+
ApplicationSet: appSet1,
673+
}
674+
675+
res, err := appSetServer.Generate(t.Context(), &appsetQuery)
676+
require.NoError(t, err)
677+
require.Len(t, res.Applications, 2)
678+
assert.Equal(t, "fake-cluster", res.Applications[0].Name)
679+
assert.Equal(t, "in-cluster", res.Applications[1].Name)
680+
})
681+
682+
t.Run("Generate in different namespace", func(t *testing.T) {
683+
appSetServer := newTestAppSetServer(t, appSet1)
684+
685+
appSet1Ns := appSet1.DeepCopy()
686+
appSet1Ns.Namespace = "external-namespace"
687+
appsetQuery := applicationset.ApplicationSetGenerateRequest{ApplicationSet: appSet1Ns}
688+
689+
res, err := appSetServer.Generate(t.Context(), &appsetQuery)
690+
require.NoError(t, err)
691+
require.Len(t, res.Applications, 2)
692+
assert.Equal(t, "fake-cluster", res.Applications[0].Name)
693+
assert.Equal(t, "in-cluster", res.Applications[1].Name)
694+
})
695+
696+
t.Run("Generate in not allowed namespace", func(t *testing.T) {
697+
appSetServer := newTestAppSetServer(t, appSet1)
698+
699+
appSet1Ns := appSet1.DeepCopy()
700+
appSet1Ns.Namespace = "NOT-ALLOWED"
701+
702+
appsetQuery := applicationset.ApplicationSetGenerateRequest{ApplicationSet: appSet1Ns}
703+
704+
_, err := appSetServer.Generate(t.Context(), &appsetQuery)
705+
assert.EqualError(t, err, "namespace 'NOT-ALLOWED' is not permitted")
706+
})
707+
}

0 commit comments

Comments
 (0)