Repository: argoproj-labs/applicationset
Pull request #26 (2020-09-01 22:36:07)

Refactor - Making testing & writing generators more easy
1. Extract common parts from the generators. Now a generator only creates the params for the application, and the rendering is happing outside.
Generator now has only one unique responsibility.

2. Refactor the if-else generator chooser to a simpler loop, and move the generators list to outside the function.
Making the code more clean, easy to test, and easy to add a new generator

This PR is going to brake every PR, so it will be better to merge: #25 , #24 , #21 before this one


Patch:
diff --git a/main.go b/main.go
index f2acbaf..8aabb5c 100644
--- a/main.go
+++ b/main.go
@@ -3,6 +3,7 @@ package main
 import (
 	"context"
 	"flag"
+	"github.com/argoproj-labs/applicationset/pkg/generators"
 	"github.com/argoproj-labs/applicationset/pkg/services"
 	argov1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
 	"os"
@@ -66,10 +67,14 @@ func main() {
 	k8s := kubernetes.NewForConfigOrDie(mgr.GetConfig())
 
 	if err = (&controllers.ApplicationSetReconciler{
+		Generators: []generators.Generator{
+			generators.NewListGenerator(),
+			generators.NewClusterGenerator(mgr.GetClient()),
+			generators.NewGitGenerator(services.NewArgoCDService(context.Background(), k8s, namespace, argocdRepoServer)),
+		},
 		Client:      mgr.GetClient(),
 		Scheme:      mgr.GetScheme(),
 		Recorder:    mgr.GetEventRecorderFor("applicationset-controller"),
-		AppsService: services.NewArgoCDService(context.Background(), k8s, namespace, argocdRepoServer),
 	}).SetupWithManager(mgr); err != nil {
 		setupLog.Error(err, "unable to create controller", "controller", "ApplicationSet")
 		os.Exit(1)
diff --git a/pkg/controllers/applicationset_controller.go b/pkg/controllers/applicationset_controller.go
index 344d04e..1144bd9 100644
--- a/pkg/controllers/applicationset_controller.go
+++ b/pkg/controllers/applicationset_controller.go
@@ -20,7 +20,7 @@ import (
 	"context"
 	"fmt"
 	"github.com/argoproj-labs/applicationset/pkg/generators"
-	"github.com/argoproj-labs/applicationset/pkg/services"
+	"github.com/argoproj-labs/applicationset/pkg/utils"
 	argov1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -41,10 +41,10 @@ import (
 // ApplicationSetReconciler reconciles a ApplicationSet object
 type ApplicationSetReconciler struct {
 	client.Client
-	Scheme         	*runtime.Scheme
-	Recorder       	record.EventRecorder
-	RepoServerAddr 	string
-	AppsService		services.Apps
+	Scheme   *runtime.Scheme
+	Recorder record.EventRecorder
+	Generators []generators.Generator
+	utils.Renderer
 }
 
 // +kubebuilder:rbac:groups=argoproj.io,resources=applicationsets,verbs=get;list;watch;create;update;patch;delete
@@ -61,32 +61,8 @@ func (r *ApplicationSetReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err
 		return ctrl.Result{}, client.IgnoreNotFound(err)
 	}
 
-	listGenerator := generators.NewListGenerator()
-	clusterGenerator := generators.NewClusterGenerator(r.Client)
-	GitGenerator := generators.NewGitGenerator(r.AppsService)
-
 	// desiredApplications is the main list of all expected Applications from all generators in this appset.
-	var desiredApplications []argov1alpha1.Application
-
-	for _, tmpGenerator := range applicationSetInfo.Spec.Generators {
-		var apps []argov1alpha1.Application
-		var err error
-		if tmpGenerator.List != nil {
-			apps, err = listGenerator.GenerateApplications(&tmpGenerator, &applicationSetInfo)
-		} else if tmpGenerator.Clusters != nil {
-			apps, err = clusterGenerator.GenerateApplications(&tmpGenerator, &applicationSetInfo)
-		} else if tmpGenerator.Git != nil {
-			apps, err = GitGenerator.GenerateApplications(&tmpGenerator, &applicationSetInfo)
-		}
-		log.Infof("apps from generator: %+v", apps)
-		if err != nil {
-			log.WithError(err).Error("error generating applications")
-			continue
-		}
-		
-		desiredApplications = append(desiredApplications, apps...)
-
-	}
+	desiredApplications := r.generateApplications(applicationSetInfo)
 
 	r.createOrUpdateInCluster(ctx, applicationSetInfo, desiredApplications)
 	r.deleteInCluster(ctx, applicationSetInfo, desiredApplications)
@@ -94,6 +70,47 @@ func (r *ApplicationSetReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err
 	return ctrl.Result{}, nil
 }
 
+func getTempApplication(applicationSetTemplate argoprojiov1alpha1.ApplicationSetTemplate) *argov1alpha1.Application{
+	var tmplApplication argov1alpha1.Application
+	tmplApplication.Labels = applicationSetTemplate.Labels
+	tmplApplication.Namespace = applicationSetTemplate.Namespace
+	tmplApplication.Name = applicationSetTemplate.Name
+	tmplApplication.Spec = applicationSetTemplate.Spec
+
+	return &tmplApplication
+}
+
+func (r *ApplicationSetReconciler) generateApplications(applicationSetInfo argoprojiov1alpha1.ApplicationSet) []argov1alpha1.Application {
+	res := []argov1alpha1.Application{}
+
+	tmplApplication := getTempApplication(applicationSetInfo.Spec.Template)
+	for _, requestedGenerator := range applicationSetInfo.Spec.Generators {
+		for _, g := range r.Generators {
+			params, err := g.GenerateParams(&requestedGenerator)
+			if err != nil {
+				log.WithError(err).WithField("generator", g).
+					Error("error generating params")
+				continue
+			}
+
+			for _, p := range params {
+				app, err := r.Renderer.RenderTemplateParams(tmplApplication, p)
+				if err != nil {
+					log.WithError(err).WithField("params", params).WithField("generator", g).
+						Error("error generating application from params")
+					continue
+				}
+				res = append(res, *app)
+			}
+
+			log.WithField("generator", g).Infof("generated %d applications", len(res))
+			log.WithField("generator", g).Debugf("apps from generator: %+v", res)
+
+		}
+	}
+	return res
+}
+
 func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
 	if err := mgr.GetFieldIndexer().IndexField(&argov1alpha1.Application{}, ".metadata.controller", func(rawObj runtime.Object) []string {
 		// grab the job object, extract the owner...
diff --git a/pkg/generators/cluster.go b/pkg/generators/cluster.go
index bbc3422..8fc1bab 100644
--- a/pkg/generators/cluster.go
+++ b/pkg/generators/cluster.go
@@ -11,8 +11,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	argoprojiov1alpha1 "github.com/argoproj-labs/applicationset/api/v1alpha1"
-	"github.com/argoproj-labs/applicationset/pkg/utils"
-	argov1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
 )
 
 const (
@@ -27,6 +25,7 @@ type ClusterGenerator struct {
 	client.Client
 }
 
+
 func NewClusterGenerator(c client.Client) Generator {
 	g := &ClusterGenerator{
 		Client: c,
@@ -34,15 +33,15 @@ func NewClusterGenerator(c client.Client) Generator {
 	return g
 }
 
-func (g *ClusterGenerator) GenerateApplications(
-	appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator,
-	appSet *argoprojiov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) {
+func (g *ClusterGenerator) GenerateParams(
+	appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) ([]map[string]string, error) {
 
 	if appSetGenerator == nil {
-		return nil, fmt.Errorf("ApplicationSetGenerator is empty")
+		return nil, EmptyAppSetGeneratorError
 	}
-	if appSet == nil {
-		return nil, fmt.Errorf("ApplicationSet is empty")
+
+	if appSetGenerator.Clusters == nil {
+		return nil, nil
 	}
 
 	// List all Clusters:
@@ -59,29 +58,18 @@ func (g *ClusterGenerator) GenerateApplications(
 	}
 	log.Debug("clusters matching labels", "count", len(clusterSecretList.Items))
 
-	var tmplApplication argov1alpha1.Application
-	tmplApplication.Labels = appSet.Spec.Template.Labels
-	tmplApplication.Namespace = appSet.Spec.Template.Namespace
-	tmplApplication.Name = appSet.Spec.Template.Name
-	tmplApplication.Spec = appSet.Spec.Template.Spec
-
-	var resultingApplications []argov1alpha1.Application
-
-	for _, cluster := range clusterSecretList.Items {
-		params := make(map[string]string)
+	res := make([]map[string]string, len(clusterSecretList.Items))
+	for i, cluster := range clusterSecretList.Items {
+		params := make(map[string]string, len(cluster.ObjectMeta.Labels) + 2)
 		params["name"] = string(cluster.Data["name"])
 		params["server"] = string(cluster.Data["server"])
 		for key, value := range cluster.ObjectMeta.Labels {
 			params[fmt.Sprintf("metadata.labels.%s", key)] = value
 		}
 		log.WithField("cluster", cluster.Name).Info("matched cluster secret")
-		tmpApplication, err := utils.RenderTemplateParams(&tmplApplication, params)
-		if err != nil {
-			log.WithField("cluster", cluster.Name).Error("Error during rendering template params")
-			continue
-		}
-		resultingApplications = append(resultingApplications, *tmpApplication)
+
+		res[i] = params
 	}
 
-	return resultingApplications, nil
+	return res, nil
 }
diff --git a/pkg/generators/git.go b/pkg/generators/git.go
index 0c07c9e..e3a4270 100644
--- a/pkg/generators/git.go
+++ b/pkg/generators/git.go
@@ -2,11 +2,8 @@ package generators
 
 import (
 	"context"
-	"fmt"
 	argoprojiov1alpha1 "github.com/argoproj-labs/applicationset/api/v1alpha1"
 	"github.com/argoproj-labs/applicationset/pkg/services"
-	"github.com/argoproj-labs/applicationset/pkg/utils"
-	argov1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
 	log "github.com/sirupsen/logrus"
 	"path"
 )
@@ -24,13 +21,14 @@ func NewGitGenerator(repos services.Apps) Generator {
 	return g
 }
 
-func (g *GitGenerator) GenerateApplications(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) {
-	if appSetGenerator == nil || appSet == nil {
-		return nil, fmt.Errorf("ApplicationSet is empty")
+func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) ([]map[string]string, error) {
+
+	if appSetGenerator == nil {
+		return nil, EmptyAppSetGeneratorError
 	}
 
 	if appSetGenerator.Git == nil {
-		return nil, fmt.Errorf("git variable empty")
+		return nil, EmptyAppSetGeneratorError
 	}
 
 	allApps, err := g.repos.GetApps(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision)
@@ -47,7 +45,7 @@ func (g *GitGenerator) GenerateApplications(appSetGenerator *argoprojiov1alpha1.
 
 	requestedApps := g.filter(appSetGenerator.Git.Directories, allApps)
 
-	res := g.generateApplications(requestedApps, appSet)
+	res := g.generateParams(requestedApps, appSetGenerator)
 
 	return res, nil
 }
@@ -70,33 +68,17 @@ func (g *GitGenerator) filter(Directories []argoprojiov1alpha1.GitDirectoryGener
 	return res
 }
 
-func (g *GitGenerator) generateApplications(requestedApps []string, appSet *argoprojiov1alpha1.ApplicationSet) ([]argov1alpha1.Application) {
+func (g *GitGenerator) generateParams(requestedApps []string, appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) []map[string]string {
 
-	res := make([]argov1alpha1.Application, len(requestedApps))
+	res := make([]map[string]string, len(requestedApps))
 	for i, a := range requestedApps {
-		app, err := g.generateApplication(appSet, a)
-		if err != nil {
-			log.WithError(err).WithField("app", a).Error("error while generating app")
-			continue
-		}
-		res[i] = *app
-	}
-
-	return res
-}
-
-
-func (g *GitGenerator) generateApplication(appSet *argoprojiov1alpha1.ApplicationSet, appPath string) (*argov1alpha1.Application, error) {
-	var tmplApplication argov1alpha1.Application
-	tmplApplication.Namespace = appSet.Spec.Template.Namespace
-	tmplApplication.Name = appSet.Spec.Template.Name
-	tmplApplication.Spec = appSet.Spec.Template.Spec
 
-	params := make(map[string]string, 2)
-	params["path"] = appPath
-	params["path.basename"] = path.Base(appPath)
+		params := make(map[string]string, 2)
+		params["path"] = a
+		params["path.basename"] = path.Base(a)
 
-	tmpApplication, err := utils.RenderTemplateParams(&tmplApplication, params)
+		res[i] = params
+	}
 
-	return tmpApplication, err
+	return res
 }
diff --git a/pkg/generators/interface.go b/pkg/generators/interface.go
index 14d0eda..f454b6b 100644
--- a/pkg/generators/interface.go
+++ b/pkg/generators/interface.go
@@ -1,15 +1,18 @@
 package generators
 
 import (
+	"errors"
 	argoprojiov1alpha1 "github.com/argoproj-labs/applicationset/api/v1alpha1"
-	argov1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
 )
 
 // Generator defines the interface implemented by all ApplicationSet generators.
 type Generator interface {
-	// GenerateApplications interprets the ApplicationSet and generates all relevant Applications.
-	// The expected / desired list of Applications is returned, it then needs to be reconciled
+	// GenerateParams interprets the ApplicationSet and generates all relevant parameters for the application template.
+	// This function will be called for every generator,
+	//even if there isn't any application - in this case the result should be nil without an error
+	// The expected / desired list of parameters is returned, it then needs to be render and reconciled
 	// against the current state of the Applications in the cluster.
-	GenerateApplications(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator,
-		appSet *argoprojiov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error)
+	GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) ([]map[string]string, error)
 }
+
+var EmptyAppSetGeneratorError = errors.New("ApplicationSet is empty")
\ No newline at end of file
diff --git a/pkg/generators/list.go b/pkg/generators/list.go
index 1c48df4..2662949 100644
--- a/pkg/generators/list.go
+++ b/pkg/generators/list.go
@@ -1,17 +1,14 @@
 package generators
 
 import (
-	"fmt"
-
 	argoprojiov1alpha1 "github.com/argoproj-labs/applicationset/api/v1alpha1"
 	"github.com/argoproj-labs/applicationset/pkg/utils"
-	argov1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
-	log "github.com/sirupsen/logrus"
 )
 
 var _ Generator = (*ListGenerator)(nil)
 
 type ListGenerator struct {
+
 }
 
 func NewListGenerator() Generator {
@@ -19,35 +16,23 @@ func NewListGenerator() Generator {
 	return g
 }
 
-func (g *ListGenerator) GenerateApplications(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator,
-	appSet *argoprojiov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) {
-	if appSetGenerator == nil || appSet == nil {
-		return nil, fmt.Errorf("ApplicationSet is empty")
+func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) ([]map[string]string, error) {
+	if appSetGenerator == nil {
+		return nil, EmptyAppSetGeneratorError
 	}
 
 	if appSetGenerator.List == nil {
 		return nil, nil
 	}
 
-	listGenerator := appSetGenerator.List
-
-	if listGenerator == nil {
-		return nil, fmt.Errorf("Empty list generator ")
-	}
-
-	var tmplApplication argov1alpha1.Application
-	tmplApplication.Namespace = appSet.Spec.Template.Namespace
-	tmplApplication.Name = appSet.Spec.Template.Name
-	tmplApplication.Spec = appSet.Spec.Template.Spec
+	res := make([]map[string]string, len(appSetGenerator.List.Elements))
 
-	var resultingApplications []argov1alpha1.Application
 	params := make(map[string]string, 2)
-	for _, tmpItem := range listGenerator.Elements {
+	for _, tmpItem := range appSetGenerator.List.Elements {
 		params[utils.ClusterListGeneratorKeyName] = tmpItem.Cluster
 		params[utils.UrlGeneratorKeyName] = tmpItem.Url
-		tmpApplication, err := utils.RenderTemplateParams(&tmplApplication, params)
-		log.Debugf("tmpApplication %++v", tmpApplication)
-		log.Debugf("error %v", err)
+		res = append(res, params)
 	}
-	return resultingApplications, nil
+
+	return res, nil
 }
diff --git a/pkg/utils/util.go b/pkg/utils/util.go
index a3bf3ab..d049c39 100644
--- a/pkg/utils/util.go
+++ b/pkg/utils/util.go
@@ -10,7 +10,15 @@ import (
 	"strconv"
 )
 
-func RenderTemplateParams(tmpl *argov1alpha1.Application, params map[string]string) (*argov1alpha1.Application, error) {
+type Renderer interface {
+	RenderTemplateParams(tmpl *argov1alpha1.Application, params map[string]string) (*argov1alpha1.Application, error)
+}
+
+type Render struct {
+
+}
+
+func (r *Render) RenderTemplateParams(tmpl *argov1alpha1.Application, params map[string]string) (*argov1alpha1.Application, error) {
 	if tmpl == nil {
 		return nil, fmt.Errorf("Application template is empty ")
 	}
@@ -25,7 +33,7 @@ func RenderTemplateParams(tmpl *argov1alpha1.Application, params map[string]stri
 	}
 
 	fstTmpl := fasttemplate.New(string(tmplBytes), "{{", "}}")
-	replacedTmplStr, err := Replace(fstTmpl, params, true)
+	replacedTmplStr, err := r.replace(fstTmpl, params, true)
 	if err != nil {
 		return nil, err
 	}
@@ -48,7 +56,7 @@ func RenderTemplateParams(tmpl *argov1alpha1.Application, params map[string]stri
 // allowUnresolved indicates whether or not it is acceptable to have unresolved variables
 // remaining in the substituted template. prefixFilter will apply the replacements only
 // to variables with the specified prefix
-func Replace(fstTmpl *fasttemplate.Template, replaceMap map[string]string, allowUnresolved bool) (string, error) {
+func (r *Render) replace(fstTmpl *fasttemplate.Template, replaceMap map[string]string, allowUnresolved bool) (string, error) {
 	var unresolvedErr error
 	replacedTmpl := fstTmpl.ExecuteFuncString(func(w io.Writer, tag string) (int, error) {
 		replacement, ok := replaceMap[tag]


Test patch:
diff --git a/pkg/controllers/applicationset_controller_test.go b/pkg/controllers/applicationset_controller_test.go
index a545731..3c969bb 100644
--- a/pkg/controllers/applicationset_controller_test.go
+++ b/pkg/controllers/applicationset_controller_test.go
@@ -2,9 +2,12 @@ package controllers
 
 import (
 	"context"
+	"errors"
 	"fmt"
+	"github.com/argoproj-labs/applicationset/pkg/generators"
 	argov1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/tools/record"
@@ -16,7 +19,149 @@ import (
 	argoprojiov1alpha1 "github.com/argoproj-labs/applicationset/api/v1alpha1"
 )
 
-func TestCreateOrUpdateApplications(t *testing.T) {
+type generatorMock struct {
+	mock.Mock
+}
+
+func (g *generatorMock) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) ([]map[string]string, error) {
+	args := g.Called(appSetGenerator)
+
+	return args.Get(0).([]map[string]string), args.Error(1)
+}
+
+type rendererMock struct {
+	mock.Mock
+}
+
+func (r *rendererMock) RenderTemplateParams(tmpl *argov1alpha1.Application, params map[string]string) (*argov1alpha1.Application, error) {
+	args := r.Called(tmpl, params)
+
+	if args.Error(1) != nil {
+		return nil, args.Error(1)
+	}
+
+	return args.Get(0).(*argov1alpha1.Application), args.Error(1)
+
+}
+
+func TestExtractApplications(t *testing.T) {
+	scheme := runtime.NewScheme()
+	argoprojiov1alpha1.AddToScheme(scheme)
+	argov1alpha1.AddToScheme(scheme)
+
+	client := fake.NewFakeClientWithScheme(scheme)
+
+	for _, c := range []struct {
+		name				string
+		params				[]map[string]string
+		template			argoprojiov1alpha1.ApplicationSetTemplate
+		generateParamsError	error
+		rendererError		error
+	}{
+		{
+			name: 		"Generate two applications",
+			params: 	[]map[string]string{{"name": "app1"}, {"name": "app2"}},
+			template:	argoprojiov1alpha1.ApplicationSetTemplate{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:                       "name",
+					Namespace:                  "namespace",
+					Labels:                     map[string]string{ "label_name": "label_value"},
+				},
+				Spec:       argov1alpha1.ApplicationSpec{
+
+				},
+			},
+		},
+		{
+			name: 		"Handles error for the generator",
+			generateParamsError: errors.New("error"),
+		},
+		{
+			name: 		"Handles error from the render",
+			params: 	[]map[string]string{{"name": "app1"}, {"name": "app2"}},
+			template:	argoprojiov1alpha1.ApplicationSetTemplate{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:                       "name",
+					Namespace:                  "namespace",
+					Labels:                     map[string]string{ "label_name": "label_value"},
+				},
+				Spec:       argov1alpha1.ApplicationSpec{
+
+				},
+			},
+			rendererError: errors.New("error"),
+		},
+	}{
+		cc := c
+		app := argov1alpha1.Application{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:                       "test",
+			},
+		}
+
+		t.Run(cc.name, func(t *testing.T) {
+
+			generatorMock := generatorMock{}
+			generator := argoprojiov1alpha1.ApplicationSetGenerator{
+				List: &argoprojiov1alpha1.ListGenerator{},
+			}
+
+			generatorMock.On("GenerateParams", &generator).
+				Return(cc.params, cc.generateParamsError)
+
+			rendererMock := rendererMock{}
+
+			expectedApps := []argov1alpha1.Application{}
+
+			if cc.generateParamsError == nil {
+				for _, p := range cc.params {
+
+					if cc.rendererError != nil {
+						rendererMock.On("RenderTemplateParams", getTempApplication(cc.template), p).
+							Return(nil, cc.rendererError)
+					} else{
+						rendererMock.On("RenderTemplateParams", getTempApplication(cc.template), p).
+							Return(&app, nil)
+						expectedApps = append(expectedApps, app)
+					}
+				}
+			}
+
+			r := ApplicationSetReconciler{
+				Client:   client,
+				Scheme:   scheme,
+				Recorder: record.NewFakeRecorder(1),
+				Generators: []generators.Generator{
+					&generatorMock,
+				},
+				Renderer: &rendererMock,
+			}
+
+			got := r.generateApplications(argoprojiov1alpha1.ApplicationSet{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "name",
+					Namespace: "namespace",
+				},
+				Spec: argoprojiov1alpha1.ApplicationSetSpec{
+					Generators: []argoprojiov1alpha1.ApplicationSetGenerator{generator},
+					Template: cc.template,
+				},
+			},)
+
+			assert.Equal(t, expectedApps, got)
+			generatorMock.AssertNumberOfCalls(t, "GenerateParams", 1)
+
+			if cc.generateParamsError == nil {
+				rendererMock.AssertNumberOfCalls(t, "RenderTemplateParams", len(cc.params))
+			}
+
+		})
+	}
+
+
+}
+
+func TestCreateOrUpdateInCluster(t *testing.T) {
 
 	scheme := runtime.NewScheme()
 	argoprojiov1alpha1.AddToScheme(scheme)
@@ -203,7 +348,7 @@ func TestCreateOrUpdateApplications(t *testing.T) {
 
 }
 
-func TestDeleteApplications(t *testing.T) {
+func TestDeleteInCluster(t *testing.T) {
 
 	scheme := runtime.NewScheme()
 	argoprojiov1alpha1.AddToScheme(scheme)
diff --git a/pkg/generators/cluster_test.go b/pkg/generators/cluster_test.go
index b30d6f9..803a1d3 100644
--- a/pkg/generators/cluster_test.go
+++ b/pkg/generators/cluster_test.go
@@ -4,8 +4,6 @@ import (
 	"context"
 	"encoding/base64"
 	"errors"
-	"fmt"
-
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -14,8 +12,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	argoprojiov1alpha1 "github.com/argoproj-labs/applicationset/api/v1alpha1"
-	argov1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
-
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -33,34 +29,7 @@ func (p *possiblyErroringFakeCtrlRuntimeClient) List(ctx context.Context, secret
 	return p.Client.List(ctx, secretList, opts...)
 }
 
-func getRenderTemplate(appName, clusterName, server, environmentLabel string) *argov1alpha1.Application {
-	return &argov1alpha1.Application{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      fmt.Sprintf("%s-%s", clusterName, appName),
-			Namespace: "namespace",
-			Finalizers: []string{
-				"resources-finalizer.argocd.argoproj.io",
-			},
-			Labels: map[string]string{
-				"environment": environmentLabel,
-			},
-		},
-		Spec: argov1alpha1.ApplicationSpec{
-			Source: argov1alpha1.ApplicationSource{
-				RepoURL:        "RepoURL",
-				Path:           fmt.Sprintf("%s/%s", appName, environmentLabel),
-				TargetRevision: "HEAD",
-			},
-			Destination: argov1alpha1.ApplicationDestination{
-				Server:    server,
-				Namespace: "destinationNamespace",
-			},
-			Project: "project",
-		},
-	}
-}
-
-func TestGenerateApplications(t *testing.T) {
+func TestGenerateParams(t *testing.T) {
 	clusters := []runtime.Object{
 		&corev1.Secret{
 			TypeMeta: metav1.TypeMeta{
@@ -105,61 +74,34 @@ func TestGenerateApplications(t *testing.T) {
 			Type: corev1.SecretType("Opaque"),
 		},
 	}
-
-	applicationSetTemplate := argoprojiov1alpha1.ApplicationSetTemplate{
-		ObjectMeta: metav1.ObjectMeta{
-			Labels: map[string]string{
-				"environment": "{{metadata.labels.environment}}",
-			},
-			Name:      "{{name}}-app",
-			Namespace: "namespace",
-		},
-		Spec: argov1alpha1.ApplicationSpec{
-			Source: argov1alpha1.ApplicationSource{
-				RepoURL:        "RepoURL",
-				Path:           "app/{{metadata.labels.environment}}",
-				TargetRevision: "HEAD",
-			},
-			Destination: argov1alpha1.ApplicationDestination{
-				Server:    "{{server}}",
-				Namespace: "destinationNamespace",
-			},
-			Project: "project",
-		},
-	}
-
 	testCases := []struct {
-		template      argoprojiov1alpha1.ApplicationSetTemplate
 		selector      metav1.LabelSelector
-		expected      []argov1alpha1.Application
+		expected      []map[string]string
 		clientError   bool
 		expectedError error
 	}{
 		{
-			applicationSetTemplate,
 			metav1.LabelSelector{},
-			[]argov1alpha1.Application{
-				*getRenderTemplate("app", "staging-01", "https://staging-01.example.com", "staging"),
-				*getRenderTemplate("app", "production-01", "https://production-01.example.com", "production"),
+			[]map[string]string{
+				{"name": "c3RhZ2luZy0wMQ==", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org":"foo", "metadata.labels.argocd.argoproj.io/secret-type": "cluster"},
+				{"name": "cHJvZHVjdGlvbi0wMQ==", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org":"bar", "metadata.labels.argocd.argoproj.io/secret-type": "cluster"},
 			},
 			false,
 			nil,
 		},
 		{
-			applicationSetTemplate,
 			metav1.LabelSelector{
 				MatchLabels: map[string]string{
 					"environment": "production",
 				},
 			},
-			[]argov1alpha1.Application{
-				*getRenderTemplate("app", "production-01", "https://production-01.example.com", "production"),
+			[]map[string]string{
+				{"name": "cHJvZHVjdGlvbi0wMQ==", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org":"bar", "metadata.labels.argocd.argoproj.io/secret-type": "cluster"},
 			},
 			false,
 			nil,
 		},
 		{
-			applicationSetTemplate,
 			metav1.LabelSelector{
 				MatchExpressions: []metav1.LabelSelectorRequirement{
 					{
@@ -172,15 +114,14 @@ func TestGenerateApplications(t *testing.T) {
 					},
 				},
 			},
-			[]argov1alpha1.Application{
-				*getRenderTemplate("app", "staging-01", "https://staging-01.example.com", "staging"),
-				*getRenderTemplate("app", "production-01", "https://production-01.example.com", "production"),
+			[]map[string]string{
+				{"name": "c3RhZ2luZy0wMQ==", "server": "https://staging-01.example.com", "metadata.labels.argocd.argoproj.io/secret-type":"cluster", "metadata.labels.environment": "staging", "metadata.labels.org":"foo"},
+				{"name": "cHJvZHVjdGlvbi0wMQ==", "server": "https://production-01.example.com", "metadata.labels.argocd.argoproj.io/secret-type":"cluster", "metadata.labels.environment": "production", "metadata.labels.org":"bar"},
 			},
 			false,
 			nil,
 		},
 		{
-			applicationSetTemplate,
 			metav1.LabelSelector{
 				MatchExpressions: []metav1.LabelSelectorRequirement{
 					{
@@ -196,14 +137,13 @@ func TestGenerateApplications(t *testing.T) {
 					"org": "foo",
 				},
 			},
-			[]argov1alpha1.Application{
-				*getRenderTemplate("app", "staging-01", "https://staging-01.example.com", "staging"),
+			[]map[string]string{
+				{"name": "c3RhZ2luZy0wMQ==", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org":"foo", "metadata.labels.argocd.argoproj.io/secret-type":"cluster"},
 			},
 			false,
 			nil,
 		},
 		{
-			applicationSetTemplate,
 			metav1.LabelSelector{},
 			nil,
 			true,
@@ -219,21 +159,12 @@ func TestGenerateApplications(t *testing.T) {
 		}
 
 		var clusterGenerator = NewClusterGenerator(cl)
-		applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
-			ObjectMeta: metav1.ObjectMeta{
-				Name: "set",
-			},
-			Spec: argoprojiov1alpha1.ApplicationSetSpec{
-				Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
-					Clusters: &argoprojiov1alpha1.ClusterGenerator{
-						Selector: testCase.selector,
-					},
-				}},
-				Template: testCase.template,
-			},
-		}
 
-		got, err := clusterGenerator.GenerateApplications(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
+		got, err := clusterGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
+			Clusters: &argoprojiov1alpha1.ClusterGenerator{
+				Selector: testCase.selector,
+			},
+		})
 
 		if testCase.expectedError != nil {
 			assert.Error(t, testCase.expectedError, err)
diff --git a/pkg/generators/git_test.go b/pkg/generators/git_test.go
index 6a43fb3..ecb9e94 100644
--- a/pkg/generators/git_test.go
+++ b/pkg/generators/git_test.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"fmt"
 	argoprojiov1alpha1 "github.com/argoproj-labs/applicationset/api/v1alpha1"
-	argov1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
 	"github.com/argoproj/argo-cd/reposerver/apiclient"
 	"github.com/argoproj/argo-cd/util"
 	"github.com/stretchr/testify/assert"
@@ -31,63 +30,18 @@ func (a argoCDServiceMock) GetApps(ctx context.Context, repoURL string, revision
 	return args.Get(0).([]string), args.Error(1)
 }
 
-func getGitRenderTemplate(name, path string) *argov1alpha1.Application {
-	return &argov1alpha1.Application{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: "namespace",
-			Finalizers: []string{
-				"resources-finalizer.argocd.argoproj.io",
-			},
-		},
-		Spec: argov1alpha1.ApplicationSpec{
-			Source: argov1alpha1.ApplicationSource{
-				RepoURL:        "RepoURL",
-				Path:           path,
-				TargetRevision: "HEAD",
-			},
-			Destination: argov1alpha1.ApplicationDestination{
-				Server:    "server",
-				Namespace: "destinationNamespace",
-			},
-			Project: "project",
-		},
-	}
-}
-
-func TestGitGenerateApplications(t *testing.T) {
-
-	appSetTemplate := argoprojiov1alpha1.ApplicationSetTemplate{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "{{path.basename}}",
-			Namespace: "namespace",
-		},
-		Spec: argov1alpha1.ApplicationSpec{
-			Source: argov1alpha1.ApplicationSource{
-				RepoURL:        "RepoURL",
-				Path:           "{{path}}",
-				TargetRevision: "HEAD",
-			},
-			Destination: argov1alpha1.ApplicationDestination{
-				Server:    "server",
-				Namespace: "destinationNamespace",
-			},
-			Project: "project",
-		},
-	}
+func TestGitGenerateParams(t *testing.T) {
 
 	cases := []struct {
 		name		  string
-		template      argoprojiov1alpha1.ApplicationSetTemplate
 		directories   []argoprojiov1alpha1.GitDirectoryGeneratorItem
 		repoApps      []string
 		repoError     error
-		expected      []argov1alpha1.Application
+		expected      []map[string]string
 		expectedError error
 	}{
 		{
 			name: "happy flow - created apps",
-			template: appSetTemplate,
 			directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{"*"}},
 			repoApps: []string{
 					"app1",
@@ -95,15 +49,14 @@ func TestGitGenerateApplications(t *testing.T) {
 					"p1/app3",
 			},
 			repoError: nil,
-			expected: []argov1alpha1.Application{
-				*getGitRenderTemplate("app1", "app1"),
-				*getGitRenderTemplate("app2", "app2"),
+			expected: []map[string]string{
+				{"path": "app1", "path.basename": "app1"},
+				{"path": "app2", "path.basename": "app2"},
 			},
 			expectedError: nil,
 		},
 		{
 			name: "It filters application according to the paths",
-			template: appSetTemplate,
 			directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{"p1/*"}, {"p1/*/*"}},
 			repoApps: []string{
 				"app1",
@@ -112,28 +65,26 @@ func TestGitGenerateApplications(t *testing.T) {
 				"p1/p2/p3/app4",
 			},
 			repoError: nil,
-			expected: []argov1alpha1.Application{
-				*getGitRenderTemplate("app2", "p1/app2"),
-				*getGitRenderTemplate("app3", "p1/p2/app3"),
+			expected: []map[string]string{
+				{"path": "p1/app2", "path.basename": "app2"},
+				{"path": "p1/p2/app3", "path.basename": "app3"},
 			},
 			expectedError: nil,
 		},
 		{
 			name: "handles empty response from repo server",
-			template: appSetTemplate,
 			directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{"*"}},
 			repoApps: []string{},
 			repoError: nil,
-			expected: []argov1alpha1.Application{},
+			expected: []map[string]string{},
 			expectedError:nil,
 		},
 		{
 			name: "handles error from repo server",
-			template: appSetTemplate,
 			directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{"*"}},
 			repoApps: []string{},
 			repoError: fmt.Errorf("error"),
-			expected: []argov1alpha1.Application{},
+			expected: []map[string]string{},
 			expectedError: fmt.Errorf("error"),
 		},
 	}
@@ -157,11 +108,10 @@ func TestGitGenerateApplications(t *testing.T) {
 							Directories: c.directories,
 						},
 					}},
-					Template: c.template,
 				},
 			}
 
-			got, err := gitGenerator.GenerateApplications(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
+			got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0])
 
 			if c.expectedError != nil {
 				assert.EqualError(t, err, c.expectedError.Error())