github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/cd-service/pkg/argocd/render_test.go (about)

     1  /*This file is part of kuberpult.
     2  
     3  Kuberpult is free software: you can redistribute it and/or modify
     4  it under the terms of the Expat(MIT) License as published by
     5  the Free Software Foundation.
     6  
     7  Kuberpult is distributed in the hope that it will be useful,
     8  but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    10  MIT License for more details.
    11  
    12  You should have received a copy of the MIT License
    13  along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>.
    14  
    15  Copyright 2023 freiheit.com*/
    16  
    17  package argocd
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/freiheit-com/kuberpult/pkg/ptr"
    23  	"github.com/freiheit-com/kuberpult/services/cd-service/pkg/argocd/v1alpha1"
    24  	"github.com/freiheit-com/kuberpult/services/cd-service/pkg/config"
    25  	"github.com/google/go-cmp/cmp"
    26  	godebug "github.com/kylelemons/godebug/diff"
    27  )
    28  
    29  func TestRender(t *testing.T) {
    30  	tcs := []struct {
    31  		Name           string
    32  		Destination    v1alpha1.ApplicationDestination
    33  		ExpectedResult string
    34  	}{
    35  		{
    36  			Name:        "deploy",
    37  			Destination: v1alpha1.ApplicationDestination{},
    38  			ExpectedResult: `apiVersion: argoproj.io/v1alpha1
    39  kind: Application
    40  metadata:
    41    annotations:
    42      argocd.argoproj.io/manifest-generate-paths: /environments/dev/applications/app1/manifests
    43      com.freiheit.kuberpult/application: app1
    44      com.freiheit.kuberpult/environment: dev
    45      com.freiheit.kuberpult/team: ""
    46    finalizers:
    47    - resources-finalizer.argocd.argoproj.io
    48    labels:
    49      com.freiheit.kuberpult/team: ""
    50    name: dev-app1
    51  spec:
    52    destination: {}
    53    ignoreDifferences:
    54    - group: a.b
    55      jqPathExpressions:
    56      - c
    57      - d
    58      kind: bar
    59      managedFieldsManagers:
    60      - e
    61      - f
    62      name: foo
    63    project: dev
    64    source:
    65      path: environments/dev/applications/app1/manifests
    66      repoURL: example.com/github
    67      targetRevision: main
    68    syncPolicy:
    69      automated:
    70        allowEmpty: true
    71        prune: true
    72        selfHeal: true
    73      syncOptions:
    74      - ApplyOutOfSyncOnly=true
    75  `,
    76  		},
    77  		{
    78  			Name:        "undeploy",
    79  			Destination: v1alpha1.ApplicationDestination{},
    80  			ExpectedResult: `apiVersion: argoproj.io/v1alpha1
    81  kind: Application
    82  metadata:
    83    annotations:
    84      argocd.argoproj.io/manifest-generate-paths: /environments/dev/applications/app1/manifests
    85      com.freiheit.kuberpult/application: app1
    86      com.freiheit.kuberpult/environment: dev
    87      com.freiheit.kuberpult/team: ""
    88    finalizers:
    89    - resources-finalizer.argocd.argoproj.io
    90    labels:
    91      com.freiheit.kuberpult/team: ""
    92    name: dev-app1
    93  spec:
    94    destination: {}
    95    ignoreDifferences:
    96    - group: a.b
    97      jqPathExpressions:
    98      - c
    99      - d
   100      kind: bar
   101      managedFieldsManagers:
   102      - e
   103      - f
   104      name: foo
   105    project: dev
   106    source:
   107      path: environments/dev/applications/app1/manifests
   108      repoURL: example.com/github
   109      targetRevision: main
   110    syncPolicy:
   111      automated:
   112        allowEmpty: true
   113        prune: true
   114        selfHeal: true
   115      syncOptions:
   116      - ApplyOutOfSyncOnly=true
   117  `,
   118  		},
   119  		{
   120  			Name: "namespace test",
   121  			Destination: v1alpha1.ApplicationDestination{
   122  				Namespace: "foo",
   123  			},
   124  			ExpectedResult: `apiVersion: argoproj.io/v1alpha1
   125  kind: Application
   126  metadata:
   127    annotations:
   128      argocd.argoproj.io/manifest-generate-paths: /environments/dev/applications/app1/manifests
   129      com.freiheit.kuberpult/application: app1
   130      com.freiheit.kuberpult/environment: dev
   131      com.freiheit.kuberpult/team: ""
   132    finalizers:
   133    - resources-finalizer.argocd.argoproj.io
   134    labels:
   135      com.freiheit.kuberpult/team: ""
   136    name: dev-app1
   137  spec:
   138    destination:
   139      namespace: foo
   140    ignoreDifferences:
   141    - group: a.b
   142      jqPathExpressions:
   143      - c
   144      - d
   145      kind: bar
   146      managedFieldsManagers:
   147      - e
   148      - f
   149      name: foo
   150    project: dev
   151    source:
   152      path: environments/dev/applications/app1/manifests
   153      repoURL: example.com/github
   154      targetRevision: main
   155    syncPolicy:
   156      automated:
   157        allowEmpty: true
   158        prune: true
   159        selfHeal: true
   160      syncOptions:
   161      - ApplyOutOfSyncOnly=true
   162  `,
   163  		},
   164  	}
   165  	for _, tc := range tcs {
   166  		tc := tc
   167  		t.Run(tc.Name, func(t *testing.T) {
   168  			t.Parallel()
   169  			var (
   170  				annotations       = map[string]string{}
   171  				ignoreDifferences = []v1alpha1.ResourceIgnoreDifferences{
   172  					{
   173  						Group:                 "a.b",
   174  						Kind:                  "bar",
   175  						Name:                  "foo",
   176  						JqPathExpressions:     []string{"c", "d"},
   177  						ManagedFieldsManagers: []string{"e", "f"},
   178  					},
   179  				}
   180  				destination = tc.Destination
   181  				GitUrl      = "example.com/github"
   182  				gitBranch   = "main"
   183  				env         = "dev"
   184  				appData     = AppData{
   185  					AppName: "app1",
   186  				}
   187  				syncOptions = []string{"ApplyOutOfSyncOnly=true"}
   188  			)
   189  
   190  			actualResult, err := RenderAppEnv(GitUrl, gitBranch, annotations, env, appData, destination, ignoreDifferences, syncOptions)
   191  			if err != nil {
   192  				t.Fatal(err)
   193  			}
   194  			if actualResult != tc.ExpectedResult {
   195  				t.Fatalf("unexpected argocd manifest:\ndiff:\n%s\n\n", godebug.Diff(tc.ExpectedResult, actualResult))
   196  			}
   197  		})
   198  	}
   199  }
   200  
   201  func TestRenderV1Alpha1(t *testing.T) {
   202  	tests := []struct {
   203  		name    string
   204  		config  config.EnvironmentConfig
   205  		appData []AppData
   206  		want    string
   207  		wantErr bool
   208  	}{
   209  		{
   210  			name: "without sync window",
   211  			config: config.EnvironmentConfig{
   212  				ArgoCd: &config.EnvironmentConfigArgoCd{
   213  					SyncWindows: nil,
   214  				},
   215  			},
   216  			want: `apiVersion: argoproj.io/v1alpha1
   217  kind: AppProject
   218  metadata:
   219    name: test-env
   220  spec:
   221    description: test-env
   222    destinations:
   223    - {}
   224    sourceRepos:
   225    - '*'
   226  `,
   227  		},
   228  		{
   229  			name: "with sync window without apps",
   230  			config: config.EnvironmentConfig{
   231  				ArgoCd: &config.EnvironmentConfigArgoCd{
   232  					SyncWindows: []config.ArgoCdSyncWindow{
   233  						{
   234  							Schedule: "not a valid crontab entry",
   235  							Duration: "invalid duration",
   236  							Kind:     "neither deny nor allow",
   237  							Apps:     nil,
   238  						},
   239  					},
   240  				},
   241  			},
   242  			want: `apiVersion: argoproj.io/v1alpha1
   243  kind: AppProject
   244  metadata:
   245    name: test-env
   246  spec:
   247    description: test-env
   248    destinations:
   249    - {}
   250    sourceRepos:
   251    - '*'
   252    syncWindows:
   253    - applications:
   254      - '*'
   255      duration: invalid duration
   256      kind: neither deny nor allow
   257      manualSync: true
   258      schedule: not a valid crontab entry
   259  `,
   260  		},
   261  		{
   262  			name: "with sync window with apps",
   263  			config: config.EnvironmentConfig{
   264  				ArgoCd: &config.EnvironmentConfigArgoCd{
   265  					SyncWindows: []config.ArgoCdSyncWindow{
   266  						{
   267  							Schedule: "not a valid crontab entry",
   268  							Duration: "invalid duration",
   269  							Kind:     "neither deny nor allow",
   270  							Apps: []string{
   271  								"app*",
   272  							},
   273  						},
   274  					},
   275  				},
   276  			},
   277  			want: `apiVersion: argoproj.io/v1alpha1
   278  kind: AppProject
   279  metadata:
   280    name: test-env
   281  spec:
   282    description: test-env
   283    destinations:
   284    - {}
   285    sourceRepos:
   286    - '*'
   287    syncWindows:
   288    - applications:
   289      - app*
   290      duration: invalid duration
   291      kind: neither deny nor allow
   292      manualSync: true
   293      schedule: not a valid crontab entry
   294  `,
   295  		},
   296  		{
   297  			name: "namespace unset with app",
   298  			config: config.EnvironmentConfig{
   299  				ArgoCd: &config.EnvironmentConfigArgoCd{
   300  					Destination: config.ArgoCdDestination{
   301  						Namespace:            nil,
   302  						AppProjectNamespace:  ptr.FromString("bar1"),
   303  						ApplicationNamespace: ptr.FromString("bar2"),
   304  					},
   305  				},
   306  			},
   307  			appData: []AppData{
   308  				{
   309  					AppName: "app1",
   310  				},
   311  			},
   312  			want: `apiVersion: argoproj.io/v1alpha1
   313  kind: AppProject
   314  metadata:
   315    name: test-env
   316  spec:
   317    description: test-env
   318    destinations:
   319    - namespace: bar1
   320    sourceRepos:
   321    - '*'
   322  ---
   323  apiVersion: argoproj.io/v1alpha1
   324  kind: Application
   325  metadata:
   326    annotations:
   327      argocd.argoproj.io/manifest-generate-paths: /environments/test-env/applications/app1/manifests
   328      com.freiheit.kuberpult/application: app1
   329      com.freiheit.kuberpult/environment: test-env
   330      com.freiheit.kuberpult/team: ""
   331    finalizers:
   332    - resources-finalizer.argocd.argoproj.io
   333    labels:
   334      com.freiheit.kuberpult/team: ""
   335    name: test-env-app1
   336  spec:
   337    destination:
   338      namespace: bar2
   339    project: test-env
   340    source:
   341      path: environments/test-env/applications/app1/manifests
   342      repoURL: https://git.example.com/
   343      targetRevision: branch-name
   344    syncPolicy:
   345      automated:
   346        allowEmpty: true
   347        prune: true
   348        selfHeal: true
   349  `,
   350  		},
   351  		{
   352  			name: "only set namespace for appProject",
   353  			config: config.EnvironmentConfig{
   354  				ArgoCd: &config.EnvironmentConfigArgoCd{
   355  					Destination: config.ArgoCdDestination{
   356  						Namespace:            nil,
   357  						AppProjectNamespace:  ptr.FromString("bar1"),
   358  						ApplicationNamespace: nil,
   359  					},
   360  				},
   361  			},
   362  			appData: []AppData{
   363  				{
   364  					AppName: "app1",
   365  				},
   366  			},
   367  			want: `apiVersion: argoproj.io/v1alpha1
   368  kind: AppProject
   369  metadata:
   370    name: test-env
   371  spec:
   372    description: test-env
   373    destinations:
   374    - namespace: bar1
   375    sourceRepos:
   376    - '*'
   377  ---
   378  apiVersion: argoproj.io/v1alpha1
   379  kind: Application
   380  metadata:
   381    annotations:
   382      argocd.argoproj.io/manifest-generate-paths: /environments/test-env/applications/app1/manifests
   383      com.freiheit.kuberpult/application: app1
   384      com.freiheit.kuberpult/environment: test-env
   385      com.freiheit.kuberpult/team: ""
   386    finalizers:
   387    - resources-finalizer.argocd.argoproj.io
   388    labels:
   389      com.freiheit.kuberpult/team: ""
   390    name: test-env-app1
   391  spec:
   392    destination: {}
   393    project: test-env
   394    source:
   395      path: environments/test-env/applications/app1/manifests
   396      repoURL: https://git.example.com/
   397      targetRevision: branch-name
   398    syncPolicy:
   399      automated:
   400        allowEmpty: true
   401        prune: true
   402        selfHeal: true
   403  `,
   404  		},
   405  		{
   406  			name: "namespace unset",
   407  			config: config.EnvironmentConfig{
   408  				ArgoCd: &config.EnvironmentConfigArgoCd{
   409  					Destination: config.ArgoCdDestination{
   410  						Namespace:            nil,
   411  						AppProjectNamespace:  ptr.FromString("bar1"),
   412  						ApplicationNamespace: ptr.FromString("bar2"),
   413  					},
   414  				},
   415  			},
   416  			want: `apiVersion: argoproj.io/v1alpha1
   417  kind: AppProject
   418  metadata:
   419    name: test-env
   420  spec:
   421    description: test-env
   422    destinations:
   423    - namespace: bar1
   424    sourceRepos:
   425    - '*'
   426  `,
   427  		},
   428  		{
   429  			name: "namespace precedence",
   430  			config: config.EnvironmentConfig{
   431  				ArgoCd: &config.EnvironmentConfigArgoCd{
   432  					Destination: config.ArgoCdDestination{
   433  						Namespace:            ptr.FromString("foo"),
   434  						AppProjectNamespace:  ptr.FromString("bar1"),
   435  						ApplicationNamespace: ptr.FromString("bar2"),
   436  					},
   437  				},
   438  			},
   439  			want: `apiVersion: argoproj.io/v1alpha1
   440  kind: AppProject
   441  metadata:
   442    name: test-env
   443  spec:
   444    description: test-env
   445    destinations:
   446    - namespace: foo
   447    sourceRepos:
   448    - '*'
   449  `,
   450  		},
   451  		{
   452  			name: "only namespace set",
   453  			config: config.EnvironmentConfig{
   454  				ArgoCd: &config.EnvironmentConfigArgoCd{
   455  					Destination: config.ArgoCdDestination{
   456  						Namespace:            ptr.FromString("foo"),
   457  						AppProjectNamespace:  nil,
   458  						ApplicationNamespace: nil,
   459  					},
   460  				},
   461  			},
   462  			want: `apiVersion: argoproj.io/v1alpha1
   463  kind: AppProject
   464  metadata:
   465    name: test-env
   466  spec:
   467    description: test-env
   468    destinations:
   469    - namespace: foo
   470    sourceRepos:
   471    - '*'
   472  `,
   473  		},
   474  		{
   475  			name: "with team name",
   476  			config: config.EnvironmentConfig{
   477  				ArgoCd: &config.EnvironmentConfigArgoCd{
   478  					Destination: config.ArgoCdDestination{
   479  						Namespace:            nil,
   480  						AppProjectNamespace:  ptr.FromString("bar1"),
   481  						ApplicationNamespace: nil,
   482  					},
   483  				},
   484  			},
   485  			appData: []AppData{
   486  				{
   487  					AppName:  "app1",
   488  					TeamName: "some-team",
   489  				},
   490  			},
   491  			want: `apiVersion: argoproj.io/v1alpha1
   492  kind: AppProject
   493  metadata:
   494    name: test-env
   495  spec:
   496    description: test-env
   497    destinations:
   498    - namespace: bar1
   499    sourceRepos:
   500    - '*'
   501  ---
   502  apiVersion: argoproj.io/v1alpha1
   503  kind: Application
   504  metadata:
   505    annotations:
   506      argocd.argoproj.io/manifest-generate-paths: /environments/test-env/applications/app1/manifests
   507      com.freiheit.kuberpult/application: app1
   508      com.freiheit.kuberpult/environment: test-env
   509      com.freiheit.kuberpult/team: some-team
   510    finalizers:
   511    - resources-finalizer.argocd.argoproj.io
   512    labels:
   513      com.freiheit.kuberpult/team: some-team
   514    name: test-env-app1
   515  spec:
   516    destination: {}
   517    project: test-env
   518    source:
   519      path: environments/test-env/applications/app1/manifests
   520      repoURL: https://git.example.com/
   521      targetRevision: branch-name
   522    syncPolicy:
   523      automated:
   524        allowEmpty: true
   525        prune: true
   526        selfHeal: true
   527  `,
   528  		},
   529  	}
   530  	for _, tt := range tests {
   531  		t.Run(tt.name, func(t *testing.T) {
   532  			const (
   533  				gitUrl    = "https://git.example.com/"
   534  				gitBranch = "branch-name"
   535  				env       = "test-env"
   536  			)
   537  			got, err := RenderV1Alpha1(gitUrl, gitBranch, tt.config, env, tt.appData)
   538  			if (err != nil) != tt.wantErr {
   539  				t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
   540  				return
   541  			}
   542  			if d := cmp.Diff(tt.want, string(got)); d != "" {
   543  				t.Errorf("mismatch: %s", d)
   544  			}
   545  		})
   546  	}
   547  }