github.com/argoproj/argo-cd@v1.8.7/controller/state_test.go (about)

     1  package controller
     2  
     3  import (
     4  	"encoding/json"
     5  	"io/ioutil"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/argoproj/gitops-engine/pkg/health"
    11  	synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
    12  	"github.com/argoproj/gitops-engine/pkg/utils/kube"
    13  	. "github.com/argoproj/gitops-engine/pkg/utils/testing"
    14  	"github.com/stretchr/testify/assert"
    15  	v1 "k8s.io/api/apps/v1"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    18  	"k8s.io/apimachinery/pkg/runtime"
    19  
    20  	"github.com/argoproj/argo-cd/common"
    21  	argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
    22  	"github.com/argoproj/argo-cd/reposerver/apiclient"
    23  	"github.com/argoproj/argo-cd/test"
    24  )
    25  
    26  // TestCompareAppStateEmpty tests comparison when both git and live have no objects
    27  func TestCompareAppStateEmpty(t *testing.T) {
    28  	app := newFakeApp()
    29  	data := fakeData{
    30  		manifestResponse: &apiclient.ManifestResponse{
    31  			Manifests: []string{},
    32  			Namespace: test.FakeDestNamespace,
    33  			Server:    test.FakeClusterURL,
    34  			Revision:  "abc123",
    35  		},
    36  		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
    37  	}
    38  	ctrl := newFakeController(&data)
    39  	compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
    40  	assert.NotNil(t, compRes)
    41  	assert.NotNil(t, compRes.syncStatus)
    42  	assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
    43  	assert.Len(t, compRes.resources, 0)
    44  	assert.Len(t, compRes.managedResources, 0)
    45  	assert.Len(t, app.Status.Conditions, 0)
    46  }
    47  
    48  // TestCompareAppStateMissing tests when there is a manifest defined in the repo which doesn't exist in live
    49  func TestCompareAppStateMissing(t *testing.T) {
    50  	app := newFakeApp()
    51  	data := fakeData{
    52  		apps: []runtime.Object{app},
    53  		manifestResponse: &apiclient.ManifestResponse{
    54  			Manifests: []string{PodManifest},
    55  			Namespace: test.FakeDestNamespace,
    56  			Server:    test.FakeClusterURL,
    57  			Revision:  "abc123",
    58  		},
    59  		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
    60  	}
    61  	ctrl := newFakeController(&data)
    62  	compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
    63  	assert.NotNil(t, compRes)
    64  	assert.NotNil(t, compRes.syncStatus)
    65  	assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
    66  	assert.Len(t, compRes.resources, 1)
    67  	assert.Len(t, compRes.managedResources, 1)
    68  	assert.Len(t, app.Status.Conditions, 0)
    69  }
    70  
    71  // TestCompareAppStateExtra tests when there is an extra object in live but not defined in git
    72  func TestCompareAppStateExtra(t *testing.T) {
    73  	pod := NewPod()
    74  	pod.SetNamespace(test.FakeDestNamespace)
    75  	app := newFakeApp()
    76  	key := kube.ResourceKey{Group: "", Kind: "Pod", Namespace: test.FakeDestNamespace, Name: app.Name}
    77  	data := fakeData{
    78  		manifestResponse: &apiclient.ManifestResponse{
    79  			Manifests: []string{},
    80  			Namespace: test.FakeDestNamespace,
    81  			Server:    test.FakeClusterURL,
    82  			Revision:  "abc123",
    83  		},
    84  		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
    85  			key: pod,
    86  		},
    87  	}
    88  	ctrl := newFakeController(&data)
    89  	compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
    90  	assert.NotNil(t, compRes)
    91  	assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
    92  	assert.Equal(t, 1, len(compRes.resources))
    93  	assert.Equal(t, 1, len(compRes.managedResources))
    94  	assert.Equal(t, 0, len(app.Status.Conditions))
    95  }
    96  
    97  // TestCompareAppStateHook checks that hooks are detected during manifest generation, and not
    98  // considered as part of resources when assessing Synced status
    99  func TestCompareAppStateHook(t *testing.T) {
   100  	pod := NewPod()
   101  	pod.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"})
   102  	podBytes, _ := json.Marshal(pod)
   103  	app := newFakeApp()
   104  	data := fakeData{
   105  		apps: []runtime.Object{app},
   106  		manifestResponse: &apiclient.ManifestResponse{
   107  			Manifests: []string{string(podBytes)},
   108  			Namespace: test.FakeDestNamespace,
   109  			Server:    test.FakeClusterURL,
   110  			Revision:  "abc123",
   111  		},
   112  		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   113  	}
   114  	ctrl := newFakeController(&data)
   115  	compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
   116  	assert.NotNil(t, compRes)
   117  	assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   118  	assert.Equal(t, 0, len(compRes.resources))
   119  	assert.Equal(t, 0, len(compRes.managedResources))
   120  	assert.Equal(t, 1, len(compRes.reconciliationResult.Hooks))
   121  	assert.Equal(t, 0, len(app.Status.Conditions))
   122  }
   123  
   124  // TestCompareAppStateSkipHook checks that skipped resources are detected during manifest generation, and not
   125  // considered as part of resources when assessing Synced status
   126  func TestCompareAppStateSkipHook(t *testing.T) {
   127  	pod := NewPod()
   128  	pod.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "Skip"})
   129  	podBytes, _ := json.Marshal(pod)
   130  	app := newFakeApp()
   131  	data := fakeData{
   132  		apps: []runtime.Object{app},
   133  		manifestResponse: &apiclient.ManifestResponse{
   134  			Manifests: []string{string(podBytes)},
   135  			Namespace: test.FakeDestNamespace,
   136  			Server:    test.FakeClusterURL,
   137  			Revision:  "abc123",
   138  		},
   139  		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   140  	}
   141  	ctrl := newFakeController(&data)
   142  	compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
   143  	assert.NotNil(t, compRes)
   144  	assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   145  	assert.Equal(t, 1, len(compRes.resources))
   146  	assert.Equal(t, 1, len(compRes.managedResources))
   147  	assert.Equal(t, 0, len(compRes.reconciliationResult.Hooks))
   148  	assert.Equal(t, 0, len(app.Status.Conditions))
   149  }
   150  
   151  // checks that ignore resources are detected, but excluded from status
   152  func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) {
   153  	pod := NewPod()
   154  	pod.SetAnnotations(map[string]string{common.AnnotationCompareOptions: "IgnoreExtraneous"})
   155  	app := newFakeApp()
   156  	data := fakeData{
   157  		apps: []runtime.Object{app},
   158  		manifestResponse: &apiclient.ManifestResponse{
   159  			Manifests: []string{},
   160  			Namespace: test.FakeDestNamespace,
   161  			Server:    test.FakeClusterURL,
   162  			Revision:  "abc123",
   163  		},
   164  		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   165  	}
   166  	ctrl := newFakeController(&data)
   167  
   168  	compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
   169  
   170  	assert.NotNil(t, compRes)
   171  	assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   172  	assert.Len(t, compRes.resources, 0)
   173  	assert.Len(t, compRes.managedResources, 0)
   174  	assert.Len(t, app.Status.Conditions, 0)
   175  }
   176  
   177  // TestCompareAppStateExtraHook tests when there is an extra _hook_ object in live but not defined in git
   178  func TestCompareAppStateExtraHook(t *testing.T) {
   179  	pod := NewPod()
   180  	pod.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"})
   181  	pod.SetNamespace(test.FakeDestNamespace)
   182  	app := newFakeApp()
   183  	key := kube.ResourceKey{Group: "", Kind: "Pod", Namespace: test.FakeDestNamespace, Name: app.Name}
   184  	data := fakeData{
   185  		manifestResponse: &apiclient.ManifestResponse{
   186  			Manifests: []string{},
   187  			Namespace: test.FakeDestNamespace,
   188  			Server:    test.FakeClusterURL,
   189  			Revision:  "abc123",
   190  		},
   191  		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
   192  			key: pod,
   193  		},
   194  	}
   195  	ctrl := newFakeController(&data)
   196  	compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
   197  
   198  	assert.NotNil(t, compRes)
   199  	assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   200  	assert.Equal(t, 1, len(compRes.resources))
   201  	assert.Equal(t, 1, len(compRes.managedResources))
   202  	assert.Equal(t, 0, len(compRes.reconciliationResult.Hooks))
   203  	assert.Equal(t, 0, len(app.Status.Conditions))
   204  }
   205  
   206  func toJSON(t *testing.T, obj *unstructured.Unstructured) string {
   207  	data, err := json.Marshal(obj)
   208  	assert.NoError(t, err)
   209  	return string(data)
   210  }
   211  
   212  func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) {
   213  	obj1 := NewPod()
   214  	obj1.SetNamespace(test.FakeDestNamespace)
   215  	obj2 := NewPod()
   216  	obj3 := NewPod()
   217  	obj3.SetNamespace("kube-system")
   218  	obj4 := NewPod()
   219  	obj4.SetGenerateName("my-pod")
   220  	obj4.SetName("")
   221  	obj5 := NewPod()
   222  	obj5.SetName("")
   223  	obj5.SetGenerateName("my-pod")
   224  
   225  	app := newFakeApp()
   226  	data := fakeData{
   227  		manifestResponse: &apiclient.ManifestResponse{
   228  			Manifests: []string{toJSON(t, obj1), toJSON(t, obj2), toJSON(t, obj3), toJSON(t, obj4), toJSON(t, obj5)},
   229  			Namespace: test.FakeDestNamespace,
   230  			Server:    test.FakeClusterURL,
   231  			Revision:  "abc123",
   232  		},
   233  		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
   234  			kube.GetResourceKey(obj1): obj1,
   235  			kube.GetResourceKey(obj3): obj3,
   236  		},
   237  	}
   238  	ctrl := newFakeController(&data)
   239  	compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
   240  
   241  	assert.NotNil(t, compRes)
   242  	assert.Equal(t, 1, len(app.Status.Conditions))
   243  	assert.NotNil(t, app.Status.Conditions[0].LastTransitionTime)
   244  	assert.Equal(t, argoappv1.ApplicationConditionRepeatedResourceWarning, app.Status.Conditions[0].Type)
   245  	assert.Equal(t, "Resource /Pod/fake-dest-ns/my-pod appeared 2 times among application resources.", app.Status.Conditions[0].Message)
   246  	assert.Equal(t, 4, len(compRes.resources))
   247  }
   248  
   249  var defaultProj = argoappv1.AppProject{
   250  	ObjectMeta: metav1.ObjectMeta{
   251  		Name:      "default",
   252  		Namespace: test.FakeArgoCDNamespace,
   253  	},
   254  	Spec: argoappv1.AppProjectSpec{
   255  		SourceRepos: []string{"*"},
   256  		Destinations: []argoappv1.ApplicationDestination{
   257  			{
   258  				Server:    "*",
   259  				Namespace: "*",
   260  			},
   261  		},
   262  	},
   263  }
   264  
   265  func TestSetHealth(t *testing.T) {
   266  	app := newFakeApp()
   267  	deployment := kube.MustToUnstructured(&v1.Deployment{
   268  		TypeMeta: metav1.TypeMeta{
   269  			APIVersion: "apps/v1beta1",
   270  			Kind:       "Deployment",
   271  		},
   272  		ObjectMeta: metav1.ObjectMeta{
   273  			Name:      "demo",
   274  			Namespace: "default",
   275  		},
   276  	})
   277  	ctrl := newFakeController(&fakeData{
   278  		apps: []runtime.Object{app, &defaultProj},
   279  		manifestResponse: &apiclient.ManifestResponse{
   280  			Manifests: []string{},
   281  			Namespace: test.FakeDestNamespace,
   282  			Server:    test.FakeClusterURL,
   283  			Revision:  "abc123",
   284  		},
   285  		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
   286  			kube.GetResourceKey(deployment): deployment,
   287  		},
   288  	})
   289  
   290  	compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
   291  
   292  	assert.Equal(t, compRes.healthStatus.Status, health.HealthStatusHealthy)
   293  }
   294  
   295  func TestSetHealthSelfReferencedApp(t *testing.T) {
   296  	app := newFakeApp()
   297  	unstructuredApp := kube.MustToUnstructured(app)
   298  	deployment := kube.MustToUnstructured(&v1.Deployment{
   299  		TypeMeta: metav1.TypeMeta{
   300  			APIVersion: "apps/v1beta1",
   301  			Kind:       "Deployment",
   302  		},
   303  		ObjectMeta: metav1.ObjectMeta{
   304  			Name:      "demo",
   305  			Namespace: "default",
   306  		},
   307  	})
   308  	ctrl := newFakeController(&fakeData{
   309  		apps: []runtime.Object{app, &defaultProj},
   310  		manifestResponse: &apiclient.ManifestResponse{
   311  			Manifests: []string{},
   312  			Namespace: test.FakeDestNamespace,
   313  			Server:    test.FakeClusterURL,
   314  			Revision:  "abc123",
   315  		},
   316  		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
   317  			kube.GetResourceKey(deployment):      deployment,
   318  			kube.GetResourceKey(unstructuredApp): unstructuredApp,
   319  		},
   320  	})
   321  
   322  	compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
   323  
   324  	assert.Equal(t, compRes.healthStatus.Status, health.HealthStatusHealthy)
   325  }
   326  
   327  func TestSetManagedResourcesWithOrphanedResources(t *testing.T) {
   328  	proj := defaultProj.DeepCopy()
   329  	proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{}
   330  
   331  	app := newFakeApp()
   332  	ctrl := newFakeController(&fakeData{
   333  		apps: []runtime.Object{app, proj},
   334  		namespacedResources: map[kube.ResourceKey]namespacedResource{
   335  			kube.NewResourceKey("apps", kube.DeploymentKind, app.Namespace, "guestbook"): {
   336  				ResourceNode: argoappv1.ResourceNode{
   337  					ResourceRef: argoappv1.ResourceRef{Kind: kube.DeploymentKind, Name: "guestbook", Namespace: app.Namespace},
   338  				},
   339  				AppName: "",
   340  			},
   341  		},
   342  	})
   343  
   344  	tree, err := ctrl.setAppManagedResources(app, &comparisonResult{managedResources: make([]managedResource, 0)})
   345  
   346  	assert.NoError(t, err)
   347  	assert.Equal(t, len(tree.OrphanedNodes), 1)
   348  	assert.Equal(t, "guestbook", tree.OrphanedNodes[0].Name)
   349  	assert.Equal(t, app.Namespace, tree.OrphanedNodes[0].Namespace)
   350  }
   351  
   352  func TestSetManagedResourcesWithResourcesOfAnotherApp(t *testing.T) {
   353  	proj := defaultProj.DeepCopy()
   354  	proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{}
   355  
   356  	app1 := newFakeApp()
   357  	app1.Name = "app1"
   358  	app2 := newFakeApp()
   359  	app2.Name = "app2"
   360  
   361  	ctrl := newFakeController(&fakeData{
   362  		apps: []runtime.Object{app1, app2, proj},
   363  		namespacedResources: map[kube.ResourceKey]namespacedResource{
   364  			kube.NewResourceKey("apps", kube.DeploymentKind, app2.Namespace, "guestbook"): {
   365  				ResourceNode: argoappv1.ResourceNode{
   366  					ResourceRef: argoappv1.ResourceRef{Kind: kube.DeploymentKind, Name: "guestbook", Namespace: app2.Namespace},
   367  				},
   368  				AppName: "app2",
   369  			},
   370  		},
   371  	})
   372  
   373  	tree, err := ctrl.setAppManagedResources(app1, &comparisonResult{managedResources: make([]managedResource, 0)})
   374  
   375  	assert.NoError(t, err)
   376  	assert.Equal(t, len(tree.OrphanedNodes), 0)
   377  }
   378  
   379  func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) {
   380  	proj := defaultProj.DeepCopy()
   381  	proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{}
   382  
   383  	app := newFakeApp()
   384  
   385  	ctrl := newFakeController(&fakeData{
   386  		apps: []runtime.Object{app, proj},
   387  		configMapData: map[string]string{
   388  			"resource.customizations": "invalid setting",
   389  		},
   390  	})
   391  
   392  	compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
   393  
   394  	assert.Equal(t, health.HealthStatusUnknown, compRes.healthStatus.Status)
   395  	assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status)
   396  }
   397  
   398  func TestSetManagedResourcesKnownOrphanedResourceExceptions(t *testing.T) {
   399  	proj := defaultProj.DeepCopy()
   400  	proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{}
   401  
   402  	app := newFakeApp()
   403  	app.Namespace = "default"
   404  
   405  	ctrl := newFakeController(&fakeData{
   406  		apps: []runtime.Object{app, proj},
   407  		namespacedResources: map[kube.ResourceKey]namespacedResource{
   408  			kube.NewResourceKey("apps", kube.DeploymentKind, app.Namespace, "guestbook"): {
   409  				ResourceNode: argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Group: "apps", Kind: kube.DeploymentKind, Name: "guestbook", Namespace: app.Namespace}},
   410  			},
   411  			kube.NewResourceKey("", kube.ServiceAccountKind, app.Namespace, "default"): {
   412  				ResourceNode: argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: kube.ServiceAccountKind, Name: "default", Namespace: app.Namespace}},
   413  			},
   414  			kube.NewResourceKey("", kube.ServiceKind, app.Namespace, "kubernetes"): {
   415  				ResourceNode: argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: kube.ServiceAccountKind, Name: "kubernetes", Namespace: app.Namespace}},
   416  			},
   417  		},
   418  	})
   419  
   420  	tree, err := ctrl.setAppManagedResources(app, &comparisonResult{managedResources: make([]managedResource, 0)})
   421  
   422  	assert.NoError(t, err)
   423  	assert.Len(t, tree.OrphanedNodes, 1)
   424  	assert.Equal(t, "guestbook", tree.OrphanedNodes[0].Name)
   425  }
   426  
   427  func Test_appStateManager_persistRevisionHistory(t *testing.T) {
   428  	app := newFakeApp()
   429  	ctrl := newFakeController(&fakeData{
   430  		apps: []runtime.Object{app},
   431  	})
   432  	manager := ctrl.appStateManager.(*appStateManager)
   433  	setRevisionHistoryLimit := func(value int) {
   434  		i := int64(value)
   435  		app.Spec.RevisionHistoryLimit = &i
   436  	}
   437  	addHistory := func() {
   438  		err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, metav1.Time{})
   439  		assert.NoError(t, err)
   440  	}
   441  	addHistory()
   442  	assert.Len(t, app.Status.History, 1)
   443  	addHistory()
   444  	assert.Len(t, app.Status.History, 2)
   445  	addHistory()
   446  	assert.Len(t, app.Status.History, 3)
   447  	addHistory()
   448  	assert.Len(t, app.Status.History, 4)
   449  	addHistory()
   450  	assert.Len(t, app.Status.History, 5)
   451  	addHistory()
   452  	assert.Len(t, app.Status.History, 6)
   453  	addHistory()
   454  	assert.Len(t, app.Status.History, 7)
   455  	addHistory()
   456  	assert.Len(t, app.Status.History, 8)
   457  	addHistory()
   458  	assert.Len(t, app.Status.History, 9)
   459  	addHistory()
   460  	assert.Len(t, app.Status.History, 10)
   461  	// default limit is 10
   462  	addHistory()
   463  	assert.Len(t, app.Status.History, 10)
   464  	// increase limit
   465  	setRevisionHistoryLimit(11)
   466  	addHistory()
   467  	assert.Len(t, app.Status.History, 11)
   468  	// decrease limit
   469  	setRevisionHistoryLimit(9)
   470  	addHistory()
   471  	assert.Len(t, app.Status.History, 9)
   472  
   473  	metav1NowTime := metav1.NewTime(time.Now())
   474  	err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, metav1NowTime)
   475  	assert.NoError(t, err)
   476  	assert.Equal(t, app.Status.History.LastRevisionHistory().DeployStartedAt, &metav1NowTime)
   477  }
   478  
   479  // helper function to read contents of a file to string
   480  // panics on error
   481  func mustReadFile(path string) string {
   482  	b, err := ioutil.ReadFile(path)
   483  	if err != nil {
   484  		panic(err.Error())
   485  	}
   486  	return string(b)
   487  }
   488  
   489  var signedProj = argoappv1.AppProject{
   490  	ObjectMeta: metav1.ObjectMeta{
   491  		Name:      "default",
   492  		Namespace: test.FakeArgoCDNamespace,
   493  	},
   494  	Spec: argoappv1.AppProjectSpec{
   495  		SourceRepos: []string{"*"},
   496  		Destinations: []argoappv1.ApplicationDestination{
   497  			{
   498  				Server:    "*",
   499  				Namespace: "*",
   500  			},
   501  		},
   502  		SignatureKeys: []argoappv1.SignatureKey{
   503  			{
   504  				KeyID: "4AEE18F83AFDEB23",
   505  			},
   506  		},
   507  	},
   508  }
   509  
   510  func TestSignedResponseNoSignatureRequired(t *testing.T) {
   511  	oldval := os.Getenv("ARGOCD_GPG_ENABLED")
   512  	os.Setenv("ARGOCD_GPG_ENABLED", "true")
   513  	defer os.Setenv("ARGOCD_GPG_ENABLED", oldval)
   514  	// We have a good signature response, but project does not require signed commits
   515  	{
   516  		app := newFakeApp()
   517  		data := fakeData{
   518  			manifestResponse: &apiclient.ManifestResponse{
   519  				Manifests:    []string{},
   520  				Namespace:    test.FakeDestNamespace,
   521  				Server:       test.FakeClusterURL,
   522  				Revision:     "abc123",
   523  				VerifyResult: mustReadFile("../util/gpg/testdata/good_signature.txt"),
   524  			},
   525  			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   526  		}
   527  		ctrl := newFakeController(&data)
   528  		compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
   529  		assert.NotNil(t, compRes)
   530  		assert.NotNil(t, compRes.syncStatus)
   531  		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   532  		assert.Len(t, compRes.resources, 0)
   533  		assert.Len(t, compRes.managedResources, 0)
   534  		assert.Len(t, app.Status.Conditions, 0)
   535  	}
   536  	// We have a bad signature response, but project does not require signed commits
   537  	{
   538  		app := newFakeApp()
   539  		data := fakeData{
   540  			manifestResponse: &apiclient.ManifestResponse{
   541  				Manifests:    []string{},
   542  				Namespace:    test.FakeDestNamespace,
   543  				Server:       test.FakeClusterURL,
   544  				Revision:     "abc123",
   545  				VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_bad.txt"),
   546  			},
   547  			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   548  		}
   549  		ctrl := newFakeController(&data)
   550  		compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
   551  		assert.NotNil(t, compRes)
   552  		assert.NotNil(t, compRes.syncStatus)
   553  		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   554  		assert.Len(t, compRes.resources, 0)
   555  		assert.Len(t, compRes.managedResources, 0)
   556  		assert.Len(t, app.Status.Conditions, 0)
   557  	}
   558  }
   559  
   560  func TestSignedResponseSignatureRequired(t *testing.T) {
   561  	oldval := os.Getenv("ARGOCD_GPG_ENABLED")
   562  	os.Setenv("ARGOCD_GPG_ENABLED", "true")
   563  	defer os.Setenv("ARGOCD_GPG_ENABLED", oldval)
   564  
   565  	// We have a good signature response, valid key, and signing is required - sync!
   566  	{
   567  		app := newFakeApp()
   568  		data := fakeData{
   569  			manifestResponse: &apiclient.ManifestResponse{
   570  				Manifests:    []string{},
   571  				Namespace:    test.FakeDestNamespace,
   572  				Server:       test.FakeClusterURL,
   573  				Revision:     "abc123",
   574  				VerifyResult: mustReadFile("../util/gpg/testdata/good_signature.txt"),
   575  			},
   576  			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   577  		}
   578  		ctrl := newFakeController(&data)
   579  		compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "", app.Spec.Source, false, nil)
   580  		assert.NotNil(t, compRes)
   581  		assert.NotNil(t, compRes.syncStatus)
   582  		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   583  		assert.Len(t, compRes.resources, 0)
   584  		assert.Len(t, compRes.managedResources, 0)
   585  		assert.Len(t, app.Status.Conditions, 0)
   586  	}
   587  	// We have a bad signature response and signing is required - do not sync
   588  	{
   589  		app := newFakeApp()
   590  		data := fakeData{
   591  			manifestResponse: &apiclient.ManifestResponse{
   592  				Manifests:    []string{},
   593  				Namespace:    test.FakeDestNamespace,
   594  				Server:       test.FakeClusterURL,
   595  				Revision:     "abc123",
   596  				VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_bad.txt"),
   597  			},
   598  			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   599  		}
   600  		ctrl := newFakeController(&data)
   601  		compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil)
   602  		assert.NotNil(t, compRes)
   603  		assert.NotNil(t, compRes.syncStatus)
   604  		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   605  		assert.Len(t, compRes.resources, 0)
   606  		assert.Len(t, compRes.managedResources, 0)
   607  		assert.Len(t, app.Status.Conditions, 1)
   608  	}
   609  	// We have a malformed signature response and signing is required - do not sync
   610  	{
   611  		app := newFakeApp()
   612  		data := fakeData{
   613  			manifestResponse: &apiclient.ManifestResponse{
   614  				Manifests:    []string{},
   615  				Namespace:    test.FakeDestNamespace,
   616  				Server:       test.FakeClusterURL,
   617  				Revision:     "abc123",
   618  				VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_malformed1.txt"),
   619  			},
   620  			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   621  		}
   622  		ctrl := newFakeController(&data)
   623  		compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil)
   624  		assert.NotNil(t, compRes)
   625  		assert.NotNil(t, compRes.syncStatus)
   626  		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   627  		assert.Len(t, compRes.resources, 0)
   628  		assert.Len(t, compRes.managedResources, 0)
   629  		assert.Len(t, app.Status.Conditions, 1)
   630  	}
   631  	// We have no signature response (no signature made) and signing is required - do not sync
   632  	{
   633  		app := newFakeApp()
   634  		data := fakeData{
   635  			manifestResponse: &apiclient.ManifestResponse{
   636  				Manifests:    []string{},
   637  				Namespace:    test.FakeDestNamespace,
   638  				Server:       test.FakeClusterURL,
   639  				Revision:     "abc123",
   640  				VerifyResult: "",
   641  			},
   642  			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   643  		}
   644  		ctrl := newFakeController(&data)
   645  		compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil)
   646  		assert.NotNil(t, compRes)
   647  		assert.NotNil(t, compRes.syncStatus)
   648  		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   649  		assert.Len(t, compRes.resources, 0)
   650  		assert.Len(t, compRes.managedResources, 0)
   651  		assert.Len(t, app.Status.Conditions, 1)
   652  	}
   653  
   654  	// We have a good signature and signing is required, but key is not allowed - do not sync
   655  	{
   656  		app := newFakeApp()
   657  		data := fakeData{
   658  			manifestResponse: &apiclient.ManifestResponse{
   659  				Manifests:    []string{},
   660  				Namespace:    test.FakeDestNamespace,
   661  				Server:       test.FakeClusterURL,
   662  				Revision:     "abc123",
   663  				VerifyResult: mustReadFile("../util/gpg/testdata/good_signature.txt"),
   664  			},
   665  			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   666  		}
   667  		ctrl := newFakeController(&data)
   668  		testProj := signedProj
   669  		testProj.Spec.SignatureKeys[0].KeyID = "4AEE18F83AFDEB24"
   670  		compRes := ctrl.appStateManager.CompareAppState(app, &testProj, "abc123", app.Spec.Source, false, nil)
   671  		assert.NotNil(t, compRes)
   672  		assert.NotNil(t, compRes.syncStatus)
   673  		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   674  		assert.Len(t, compRes.resources, 0)
   675  		assert.Len(t, compRes.managedResources, 0)
   676  		assert.Len(t, app.Status.Conditions, 1)
   677  		assert.Contains(t, app.Status.Conditions[0].Message, "key is not allowed")
   678  	}
   679  	// Signature required and local manifests supplied - do not sync
   680  	{
   681  		app := newFakeApp()
   682  		data := fakeData{
   683  			manifestResponse: &apiclient.ManifestResponse{
   684  				Manifests:    []string{},
   685  				Namespace:    test.FakeDestNamespace,
   686  				Server:       test.FakeClusterURL,
   687  				Revision:     "abc123",
   688  				VerifyResult: "",
   689  			},
   690  			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   691  		}
   692  		// it doesn't matter for our test whether local manifests are valid
   693  		localManifests := []string{"foobar"}
   694  		ctrl := newFakeController(&data)
   695  		compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, localManifests)
   696  		assert.NotNil(t, compRes)
   697  		assert.NotNil(t, compRes.syncStatus)
   698  		assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status)
   699  		assert.Len(t, compRes.resources, 0)
   700  		assert.Len(t, compRes.managedResources, 0)
   701  		assert.Len(t, app.Status.Conditions, 1)
   702  		assert.Contains(t, app.Status.Conditions[0].Message, "Cannot use local manifests")
   703  	}
   704  
   705  	os.Setenv("ARGOCD_GPG_ENABLED", "false")
   706  	// We have a bad signature response and signing would be required, but GPG subsystem is disabled - sync
   707  	{
   708  		app := newFakeApp()
   709  		data := fakeData{
   710  			manifestResponse: &apiclient.ManifestResponse{
   711  				Manifests:    []string{},
   712  				Namespace:    test.FakeDestNamespace,
   713  				Server:       test.FakeClusterURL,
   714  				Revision:     "abc123",
   715  				VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_bad.txt"),
   716  			},
   717  			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   718  		}
   719  		ctrl := newFakeController(&data)
   720  		compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil)
   721  		assert.NotNil(t, compRes)
   722  		assert.NotNil(t, compRes.syncStatus)
   723  		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   724  		assert.Len(t, compRes.resources, 0)
   725  		assert.Len(t, compRes.managedResources, 0)
   726  		assert.Len(t, app.Status.Conditions, 0)
   727  	}
   728  
   729  	// Signature required and local manifests supplied and GPG subystem is disabled - sync
   730  	{
   731  		app := newFakeApp()
   732  		data := fakeData{
   733  			manifestResponse: &apiclient.ManifestResponse{
   734  				Manifests:    []string{},
   735  				Namespace:    test.FakeDestNamespace,
   736  				Server:       test.FakeClusterURL,
   737  				Revision:     "abc123",
   738  				VerifyResult: "",
   739  			},
   740  			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
   741  		}
   742  		// it doesn't matter for our test whether local manifests are valid
   743  		localManifests := []string{""}
   744  		ctrl := newFakeController(&data)
   745  		compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, localManifests)
   746  		assert.NotNil(t, compRes)
   747  		assert.NotNil(t, compRes.syncStatus)
   748  		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
   749  		assert.Len(t, compRes.resources, 0)
   750  		assert.Len(t, compRes.managedResources, 0)
   751  		assert.Len(t, app.Status.Conditions, 0)
   752  	}
   753  
   754  }
   755  
   756  func TestComparisonResult_GetHealthStatus(t *testing.T) {
   757  	status := &argoappv1.HealthStatus{Status: health.HealthStatusMissing}
   758  	res := comparisonResult{
   759  		healthStatus: status,
   760  	}
   761  
   762  	assert.Equal(t, status, res.GetHealthStatus())
   763  }
   764  
   765  func TestComparisonResult_GetSyncStatus(t *testing.T) {
   766  	status := &argoappv1.SyncStatus{Status: argoappv1.SyncStatusCodeOutOfSync}
   767  	res := comparisonResult{
   768  		syncStatus: status,
   769  	}
   770  
   771  	assert.Equal(t, status, res.GetSyncStatus())
   772  }