Repository: argoproj/argo-cd
Pull request #4729 (2020-11-02 18:14:38)

fix: failUnmarshal only checks for file resembles a resource 
…as apiVersion, kind and metadata.

Signed-off-by: May Zhang <may_zhang@intuit.com>

Checklist:

* [ ] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo-cd/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
* [ ] The title of the PR states what changed and the related issues number (used for the release note).
* [ ] I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
* [ ] Does this PR require documentation updates?
* [ ] I've updated documentation as required by this PR.
* [ ] Optional. My organization is added to USERS.md.
* [ ] I've signed the CLA and my build is green ([troubleshooting builds](https://argoproj.github.io/argo-cd/developer-guide/ci/)). 



Patch:
diff --git a/Makefile b/Makefile
index a74f658bc..ca01033e4 100644
--- a/Makefile
+++ b/Makefile
@@ -383,7 +383,7 @@ start-e2e-local:
 	ARGOCD_ZJWT_FEATURE_FLAG=always \
 	ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
 	ARGOCD_E2E_TEST=true \
-		goreman -f $(ARGOCD_PROCFILE) start
+		goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
 
 # Cleans VSCode debug.test files from sub-dirs to prevent them from being included in packr boxes
 .PHONY: clean-debug
diff --git a/USERS.md b/USERS.md
index bcb6eee61..f1c92a65a 100644
--- a/USERS.md
+++ b/USERS.md
@@ -51,6 +51,7 @@ Currently, the following organizations are **officially** using Argo CD:
 1. [MOO Print](https://www.moo.com/)
 1. [Nikkei](https://www.nikkei.co.jp/nikkeiinfo/en/)
 1. [OpenSaaS Studio](https://opensaas.studio)
+1. [Opensurvey](https://www.opensurvey.co.kr/)
 1. [Optoro](https://www.optoro.com/)
 1. [Peloton Interactive](https://www.onepeloton.com/)
 1. [Pipefy](https://www.pipefy.com/)
diff --git a/assets/swagger.json b/assets/swagger.json
index eeb740e99..6c81f4694 100644
--- a/assets/swagger.json
+++ b/assets/swagger.json
@@ -188,7 +188,7 @@
           "ApplicationService"
         ],
         "summary": "List returns list of applications",
-        "operationId": "ListMixin9",
+        "operationId": "List",
         "parameters": [
           {
             "type": "string",
@@ -239,7 +239,7 @@
           "ApplicationService"
         ],
         "summary": "Create creates an application",
-        "operationId": "CreateMixin9",
+        "operationId": "Create",
         "parameters": [
           {
             "name": "body",
@@ -266,7 +266,7 @@
           "ApplicationService"
         ],
         "summary": "Update updates an application",
-        "operationId": "UpdateMixin9",
+        "operationId": "Update",
         "parameters": [
           {
             "type": "string",
@@ -400,7 +400,7 @@
           "ApplicationService"
         ],
         "summary": "Get returns an application by name",
-        "operationId": "GetMixin9",
+        "operationId": "GetMixin1",
         "parameters": [
           {
             "type": "string",
@@ -452,7 +452,7 @@
           "ApplicationService"
         ],
         "summary": "Delete deletes an application",
-        "operationId": "DeleteMixin9",
+        "operationId": "Delete",
         "parameters": [
           {
             "type": "string",
@@ -1162,7 +1162,7 @@
           "ClusterService"
         ],
         "summary": "List returns list of clusters",
-        "operationId": "List",
+        "operationId": "ListMixin5",
         "parameters": [
           {
             "type": "string",
@@ -1189,7 +1189,7 @@
           "ClusterService"
         ],
         "summary": "Create creates a cluster",
-        "operationId": "Create",
+        "operationId": "CreateMixin5",
         "parameters": [
           {
             "name": "body",
@@ -1216,7 +1216,7 @@
           "ClusterService"
         ],
         "summary": "Update updates a cluster",
-        "operationId": "Update",
+        "operationId": "UpdateMixin5",
         "parameters": [
           {
             "type": "string",
@@ -1250,7 +1250,7 @@
           "ClusterService"
         ],
         "summary": "Get returns a cluster by server address",
-        "operationId": "GetMixin2",
+        "operationId": "GetMixin5",
         "parameters": [
           {
             "type": "string",
@@ -1278,7 +1278,7 @@
           "ClusterService"
         ],
         "summary": "Delete deletes a cluster",
-        "operationId": "Delete",
+        "operationId": "DeleteMixin5",
         "parameters": [
           {
             "type": "string",
@@ -1358,7 +1358,7 @@
           "GPGKeyService"
         ],
         "summary": "List all available repository certificates",
-        "operationId": "ListMixin7",
+        "operationId": "ListMixin2",
         "parameters": [
           {
             "type": "string",
@@ -1381,7 +1381,7 @@
           "GPGKeyService"
         ],
         "summary": "Create one or more GPG public keys in the server's configuration",
-        "operationId": "CreateMixin7",
+        "operationId": "CreateMixin2",
         "parameters": [
           {
             "description": "Raw key data of the GPG key(s) to create",
@@ -1407,7 +1407,7 @@
           "GPGKeyService"
         ],
         "summary": "Delete specified GPG public key from the server's configuration",
-        "operationId": "DeleteMixin7",
+        "operationId": "DeleteMixin2",
         "parameters": [
           {
             "type": "string",
@@ -1432,7 +1432,7 @@
           "GPGKeyService"
         ],
         "summary": "Get information about specified GPG public key from the server",
-        "operationId": "GetMixin7",
+        "operationId": "GetMixin2",
         "parameters": [
           {
             "type": "string",
@@ -1948,7 +1948,7 @@
           "RepositoryService"
         ],
         "summary": "Get returns a repository or its credentials",
-        "operationId": "GetMixin3",
+        "operationId": "Get",
         "parameters": [
           {
             "type": "string",
@@ -2011,7 +2011,7 @@
         "tags": [
           "RepositoryService"
         ],
-        "summary": "ListApps returns list of apps in the repo",
+        "summary": "ListApps returns list of apps in the repe",
         "operationId": "ListApps",
         "parameters": [
           {
@@ -2235,7 +2235,7 @@
           "SettingsService"
         ],
         "summary": "Get returns Argo CD settings",
-        "operationId": "Get",
+        "operationId": "GetMixin8",
         "responses": {
           "200": {
             "description": "A successful response.",
diff --git a/cmd/argocd/commands/project.go b/cmd/argocd/commands/project.go
index b88f2fe21..dfb9d0e6a 100644
--- a/cmd/argocd/commands/project.go
+++ b/cmd/argocd/commands/project.go
@@ -564,9 +564,9 @@ func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.Clie
 		defaultList string
 	)
 	if namespacedList {
-		defaultList = "black"
+		defaultList = "deny"
 	} else {
-		defaultList = "white"
+		defaultList = "allow"
 	}
 	var command = &cobra.Command{
 		Use:   cmdUse,
@@ -582,24 +582,24 @@ func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.Clie
 
 			proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
 			errors.CheckError(err)
-			var list, white, black *[]metav1.GroupKind
+			var list, allowList, denyList *[]metav1.GroupKind
 			var listAction, listDesc string
 			var add bool
 			if namespacedList {
-				white, black = &proj.Spec.NamespaceResourceWhitelist, &proj.Spec.NamespaceResourceBlacklist
+				allowList, denyList = &proj.Spec.NamespaceResourceWhitelist, &proj.Spec.NamespaceResourceBlacklist
 				listDesc = "namespaced"
 			} else {
-				white, black = &proj.Spec.ClusterResourceWhitelist, &proj.Spec.ClusterResourceBlacklist
+				allowList, denyList = &proj.Spec.ClusterResourceWhitelist, &proj.Spec.ClusterResourceBlacklist
 				listDesc = "cluster"
 			}
 
-			if listType == "white" {
-				list = white
-				listAction = "whitelisted"
+			if (listType == "allow") || (listType == "white") {
+				list = allowList
+				listAction = "allowed"
 				add = allow
 			} else {
-				list = black
-				listAction = "blacklisted"
+				list = denyList
+				listAction = "denied"
 				add = !allow
 			}
 
@@ -609,35 +609,35 @@ func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.Clie
 			}
 		},
 	}
-	command.Flags().StringVarP(&listType, "list", "l", defaultList, "Use blacklist or whitelist. This can only be 'white' or 'black'")
+	command.Flags().StringVarP(&listType, "list", "l", defaultList, "Use deny list or allow list. This can only be 'allow' or 'deny'")
 	return command
 }
 
 // NewProjectAllowNamespaceResourceCommand returns a new instance of an `deny-cluster-resources` command
 func NewProjectAllowNamespaceResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
 	use := "allow-namespace-resource PROJECT GROUP KIND"
-	desc := "Removes a namespaced API resource from the blacklist or add a namespaced API resource to the whitelist"
+	desc := "Removes a namespaced API resource from the deny list or add a namespaced API resource to the allow list"
 	return modifyResourceListCmd(use, desc, clientOpts, true, true)
 }
 
 // NewProjectDenyNamespaceResourceCommand returns a new instance of an `argocd proj deny-namespace-resource` command
 func NewProjectDenyNamespaceResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
 	use := "deny-namespace-resource PROJECT GROUP KIND"
-	desc := "Adds a namespaced API resource to the blacklist or removes a namespaced API resource from the whitelist"
+	desc := "Adds a namespaced API resource to the deny list or removes a namespaced API resource from the allow list"
 	return modifyResourceListCmd(use, desc, clientOpts, false, true)
 }
 
 // NewProjectDenyClusterResourceCommand returns a new instance of an `deny-cluster-resource` command
 func NewProjectDenyClusterResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
 	use := "deny-cluster-resource PROJECT GROUP KIND"
-	desc := "Removes a cluster-scoped API resource from the whitelist and adds it to blacklist"
+	desc := "Removes a cluster-scoped API resource from the allow list and adds it to deny list"
 	return modifyResourceListCmd(use, desc, clientOpts, false, false)
 }
 
 // NewProjectAllowClusterResourceCommand returns a new instance of an `argocd proj allow-cluster-resource` command
 func NewProjectAllowClusterResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
 	use := "allow-cluster-resource PROJECT GROUP KIND"
-	desc := "Adds a cluster-scoped API resource to the whitelist and removes it from blacklist"
+	desc := "Adds a cluster-scoped API resource to the allow list and removes it from deny list"
 	return modifyResourceListCmd(use, desc, clientOpts, true, false)
 }
 
@@ -800,7 +800,7 @@ func printProjectLine(w io.Writer, p *v1alpha1.AppProject) {
 }
 
 func printProject(p *v1alpha1.AppProject) {
-	const printProjFmtStr = "%-34s%s\n"
+	const printProjFmtStr = "%-29s%s\n"
 
 	fmt.Printf(printProjFmtStr, "Name:", p.Name)
 	fmt.Printf(printProjFmtStr, "Description:", p.Spec.Description)
@@ -825,22 +825,22 @@ func printProject(p *v1alpha1.AppProject) {
 		fmt.Printf(printProjFmtStr, "", p.Spec.SourceRepos[i])
 	}
 
-	// Print whitelisted cluster resources
+	// Print allowed cluster resources
 	cwl0 := "<none>"
 	if len(p.Spec.ClusterResourceWhitelist) > 0 {
 		cwl0 = fmt.Sprintf("%s/%s", p.Spec.ClusterResourceWhitelist[0].Group, p.Spec.ClusterResourceWhitelist[0].Kind)
 	}
-	fmt.Printf(printProjFmtStr, "Whitelisted Cluster Resources:", cwl0)
+	fmt.Printf(printProjFmtStr, "Allowed Cluster Resources:", cwl0)
 	for i := 1; i < len(p.Spec.ClusterResourceWhitelist); i++ {
 		fmt.Printf(printProjFmtStr, "", fmt.Sprintf("%s/%s", p.Spec.ClusterResourceWhitelist[i].Group, p.Spec.ClusterResourceWhitelist[i].Kind))
 	}
 
-	// Print blacklisted namespaced resources
+	// Print denied namespaced resources
 	rbl0 := "<none>"
 	if len(p.Spec.NamespaceResourceBlacklist) > 0 {
 		rbl0 = fmt.Sprintf("%s/%s", p.Spec.NamespaceResourceBlacklist[0].Group, p.Spec.NamespaceResourceBlacklist[0].Kind)
 	}
-	fmt.Printf(printProjFmtStr, "Blacklisted Namespaced Resources:", rbl0)
+	fmt.Printf(printProjFmtStr, "Denied Namespaced Resources:", rbl0)
 	for i := 1; i < len(p.Spec.NamespaceResourceBlacklist); i++ {
 		fmt.Printf(printProjFmtStr, "", fmt.Sprintf("%s/%s", p.Spec.NamespaceResourceBlacklist[i].Group, p.Spec.NamespaceResourceBlacklist[i].Kind))
 	}
diff --git a/controller/appcontroller.go b/controller/appcontroller.go
index 52e0e70cf..565d851e0 100644
--- a/controller/appcontroller.go
+++ b/controller/appcontroller.go
@@ -228,7 +228,7 @@ func (ctrl *ApplicationController) getAppProj(app *appv1.Application) (*appv1.Ap
 func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]bool, ref v1.ObjectReference) {
 	// if namespaced resource is not managed by any app it might be orphaned resource of some other apps
 	if len(managedByApp) == 0 && ref.Namespace != "" {
-		// retrieve applications which monitor orphaned resources in the same namespace and refresh them unless resource is blacklisted in app project
+		// retrieve applications which monitor orphaned resources in the same namespace and refresh them unless resource is denied in app project
 		if objs, err := ctrl.appInformer.GetIndexer().ByIndex(orphanedIndex, ref.Namespace); err == nil {
 			for i := range objs {
 				app, ok := objs[i].(*appv1.Application)
diff --git a/controller/sync.go b/controller/sync.go
index dedfc71c3..cdef70220 100644
--- a/controller/sync.go
+++ b/controller/sync.go
@@ -3,6 +3,8 @@ package controller
 import (
 	"context"
 	"fmt"
+	"os"
+	"strconv"
 	"sync/atomic"
 	"time"
 
@@ -26,6 +28,12 @@ import (
 
 var syncIdPrefix uint64 = 0
 
+const (
+	// EnvVarSyncWaveDelay is an environment variable which controls the delay in seconds between
+	// each sync-wave
+	EnvVarSyncWaveDelay = "ARGOCD_SYNC_WAVE_DELAY"
+)
+
 func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState) {
 	// Sync requests might be requested with ambiguous revisions (e.g. master, HEAD, v1.2.3).
 	// This can change meaning when resuming operations (e.g a hook sync). After calculating a
@@ -158,6 +166,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
 			}
 			return false
 		}),
+		sync.WithSyncWaveHook(delayBetweenSyncWaves),
 	)
 
 	if err != nil {
@@ -200,3 +209,25 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
 		}
 	}
 }
+
+// delayBetweenSyncWaves is a gitops-engine SyncWaveHook which introduces an artificial delay
+// between each sync wave. We introduce an artificial delay in order give other controllers a
+// _chance_ to react to the spec change that we just applied. This is important because without
+// this, Argo CD will likely assess resource health too quickly (against the stale object), causing
+// hooks to fire prematurely. See: https://github.com/argoproj/argo-cd/issues/4669.
+// Note, this is not foolproof, since a proper fix would require the CRD record
+// status.observedGeneration coupled with a health.lua that verifies
+// status.observedGeneration == metadata.generation
+func delayBetweenSyncWaves(phase common.SyncPhase, wave int, finalWave bool) error {
+	if !finalWave {
+		delaySec := 2
+		if delaySecStr := os.Getenv(EnvVarSyncWaveDelay); delaySecStr != "" {
+			if val, err := strconv.Atoi(delaySecStr); err == nil {
+				delaySec = val
+			}
+		}
+		duration := time.Duration(delaySec) * time.Second
+		time.Sleep(duration)
+	}
+	return nil
+}
diff --git a/docs/user-guide/commands/argocd_proj.md b/docs/user-guide/commands/argocd_proj.md
index 8ea5f18ea..1098c8a5a 100644
--- a/docs/user-guide/commands/argocd_proj.md
+++ b/docs/user-guide/commands/argocd_proj.md
@@ -43,12 +43,12 @@ argocd proj [flags]
 * [argocd proj add-orphaned-ignore](argocd_proj_add-orphaned-ignore.md)	 - Add a resource to orphaned ignore list
 * [argocd proj add-signature-key](argocd_proj_add-signature-key.md)	 - Add GnuPG signature key to project
 * [argocd proj add-source](argocd_proj_add-source.md)	 - Add project source repository
-* [argocd proj allow-cluster-resource](argocd_proj_allow-cluster-resource.md)	 - Adds a cluster-scoped API resource to the whitelist and removes it from blacklist
-* [argocd proj allow-namespace-resource](argocd_proj_allow-namespace-resource.md)	 - Removes a namespaced API resource from the blacklist or add a namespaced API resource to the whitelist
+* [argocd proj allow-cluster-resource](argocd_proj_allow-cluster-resource.md)	 - Adds a cluster-scoped API resource to the allow list and removes it from deny list
+* [argocd proj allow-namespace-resource](argocd_proj_allow-namespace-resource.md)	 - Removes a namespaced API resource from the deny list or add a namespaced API resource to the allow list
 * [argocd proj create](argocd_proj_create.md)	 - Create a project
 * [argocd proj delete](argocd_proj_delete.md)	 - Delete project
-* [argocd proj deny-cluster-resource](argocd_proj_deny-cluster-resource.md)	 - Removes a cluster-scoped API resource from the whitelist and adds it to blacklist
-* [argocd proj deny-namespace-resource](argocd_proj_deny-namespace-resource.md)	 - Adds a namespaced API resource to the blacklist or removes a namespaced API resource from the whitelist
+* [argocd proj deny-cluster-resource](argocd_proj_deny-cluster-resource.md)	 - Removes a cluster-scoped API resource from the allow list and adds it to deny list
+* [argocd proj deny-namespace-resource](argocd_proj_deny-namespace-resource.md)	 - Adds a namespaced API resource to the deny list or removes a namespaced API resource from the allow list
 * [argocd proj edit](argocd_proj_edit.md)	 - Edit project
 * [argocd proj get](argocd_proj_get.md)	 - Get project details
 * [argocd proj list](argocd_proj_list.md)	 - List projects
diff --git a/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md b/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md
index f5a4e1d24..abdce755f 100644
--- a/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md
+++ b/docs/user-guide/commands/argocd_proj_allow-cluster-resource.md
@@ -1,10 +1,10 @@
 ## argocd proj allow-cluster-resource
 
-Adds a cluster-scoped API resource to the whitelist and removes it from blacklist
+Adds a cluster-scoped API resource to the allow list and removes it from deny list
 
 ### Synopsis
 
-Adds a cluster-scoped API resource to the whitelist and removes it from blacklist
+Adds a cluster-scoped API resource to the allow list and removes it from deny list
 
 ```
 argocd proj allow-cluster-resource PROJECT GROUP KIND [flags]
@@ -14,7 +14,7 @@ argocd proj allow-cluster-resource PROJECT GROUP KIND [flags]
 
 ```
   -h, --help          help for allow-cluster-resource
-  -l, --list string   Use blacklist or whitelist. This can only be 'white' or 'black' (default "white")
+  -l, --list string   Use deny list or allow list. This can only be 'allow' or 'deny' (default "allow")
 ```
 
 ### Options inherited from parent commands
diff --git a/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md b/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md
index c064f2669..9ec543b12 100644
--- a/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md
+++ b/docs/user-guide/commands/argocd_proj_allow-namespace-resource.md
@@ -1,10 +1,10 @@
 ## argocd proj allow-namespace-resource
 
-Removes a namespaced API resource from the blacklist or add a namespaced API resource to the whitelist
+Removes a namespaced API resource from the deny list or add a namespaced API resource to the allow list
 
 ### Synopsis
 
-Removes a namespaced API resource from the blacklist or add a namespaced API resource to the whitelist
+Removes a namespaced API resource from the deny list or add a namespaced API resource to the allow list
 
 ```
 argocd proj allow-namespace-resource PROJECT GROUP KIND [flags]
@@ -14,7 +14,7 @@ argocd proj allow-namespace-resource PROJECT GROUP KIND [flags]
 
 ```
   -h, --help          help for allow-namespace-resource
-  -l, --list string   Use blacklist or whitelist. This can only be 'white' or 'black' (default "black")
+  -l, --list string   Use deny list or allow list. This can only be 'allow' or 'deny' (default "deny")
 ```
 
 ### Options inherited from parent commands
diff --git a/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md b/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md
index 7f35a0bd1..1893dc14c 100644
--- a/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md
+++ b/docs/user-guide/commands/argocd_proj_deny-cluster-resource.md
@@ -1,10 +1,10 @@
 ## argocd proj deny-cluster-resource
 
-Removes a cluster-scoped API resource from the whitelist and adds it to blacklist
+Removes a cluster-scoped API resource from the allow list and adds it to deny list
 
 ### Synopsis
 
-Removes a cluster-scoped API resource from the whitelist and adds it to blacklist
+Removes a cluster-scoped API resource from the allow list and adds it to deny list
 
 ```
 argocd proj deny-cluster-resource PROJECT GROUP KIND [flags]
@@ -14,7 +14,7 @@ argocd proj deny-cluster-resource PROJECT GROUP KIND [flags]
 
 ```
   -h, --help          help for deny-cluster-resource
-  -l, --list string   Use blacklist or whitelist. This can only be 'white' or 'black' (default "white")
+  -l, --list string   Use deny list or allow list. This can only be 'allow' or 'deny' (default "allow")
 ```
 
 ### Options inherited from parent commands
diff --git a/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md b/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md
index 3a03f7e15..2bb7aa572 100644
--- a/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md
+++ b/docs/user-guide/commands/argocd_proj_deny-namespace-resource.md
@@ -1,10 +1,10 @@
 ## argocd proj deny-namespace-resource
 
-Adds a namespaced API resource to the blacklist or removes a namespaced API resource from the whitelist
+Adds a namespaced API resource to the deny list or removes a namespaced API resource from the allow list
 
 ### Synopsis
 
-Adds a namespaced API resource to the blacklist or removes a namespaced API resource from the whitelist
+Adds a namespaced API resource to the deny list or removes a namespaced API resource from the allow list
 
 ```
 argocd proj deny-namespace-resource PROJECT GROUP KIND [flags]
@@ -14,7 +14,7 @@ argocd proj deny-namespace-resource PROJECT GROUP KIND [flags]
 
 ```
   -h, --help          help for deny-namespace-resource
-  -l, --list string   Use blacklist or whitelist. This can only be 'white' or 'black' (default "black")
+  -l, --list string   Use deny list or allow list. This can only be 'allow' or 'deny' (default "deny")
 ```
 
 ### Options inherited from parent commands
diff --git a/docs/user-guide/gpg-verification.md b/docs/user-guide/gpg-verification.md
index c3cc722de..8d370d41c 100644
--- a/docs/user-guide/gpg-verification.md
+++ b/docs/user-guide/gpg-verification.md
@@ -187,14 +187,14 @@ output of the `argocd proj get` command, i.e for a project named `gpg`:
 
 ```bash
 $ argocd proj get gpg
-Name:                             gpg
-Description:                      GnuPG verification
-Destinations:                     *,*
-Repositories:                     *
-Whitelisted Cluster Resources:    */*
-Blacklisted Namespaced Resources: <none>
-Signature keys:                   4AEE18F83AFDEB23, 07E34825A909B250
-Orphaned Resources:               disabled
+Name:                        gpg
+Description:                 GnuPG verification
+Destinations:                *,*
+Repositories:                *
+Allowed Cluster Resources:   */*
+Denied Namespaced Resources: <none>
+Signature keys:              4AEE18F83AFDEB23, 07E34825A909B250
+Orphaned Resources:          disabled
 ```
 
 #### Override list of key IDs
diff --git a/docs/user-guide/orphaned-resources.md b/docs/user-guide/orphaned-resources.md
index 45ad2d70b..8e4cf0d89 100644
--- a/docs/user-guide/orphaned-resources.md
+++ b/docs/user-guide/orphaned-resources.md
@@ -14,6 +14,6 @@ Before enabling feature you might consider disabling warning. In this case appli
 
 Not every resource in the Kubernetes cluster is controlled by the end user. Following resources are never considered as orphaned:
 
-* Namespaced resources blacklisted in the project. Usually, such resources are managed by cluster administrators and not supposed to be modified by namespace user.
+* Namespaced resources denied in the project. Usually, such resources are managed by cluster administrators and not supposed to be modified by namespace user.
 * `ServiceAccount` with name `default` ( and corresponding auto-generated `ServiceAccountToken` ).
 * `Service` with name `kubernetes` in the `default` namespace.
diff --git a/docs/user-guide/parameters.md b/docs/user-guide/parameters.md
index 0e017b89b..41a2994d6 100644
--- a/docs/user-guide/parameters.md
+++ b/docs/user-guide/parameters.md
@@ -15,15 +15,15 @@ changes to the manifests in Git.
 To use parameter overrides, run the `argocd app set -p (COMPONENT=)PARAM=VALUE` command:
 
 ```bash
-argocd app set guestbook -p guestbook=image=example/guestbook:abcd123
+argocd app set guestbook -p image=example/guestbook:abcd123
 argocd app sync guestbook
 ```
 
 The `PARAM` is expected to be a normal YAML path
 
 ```bash
-argocd app set guestbook -p guestbook=ingress.enabled=true
-argocd app set guestbook -p guestbook=ingress.hosts[0]=guestbook.myclusterurl
+argocd app set guestbook -p ingress.enabled=true
+argocd app set guestbook -p ingress.hosts[0]=guestbook.myclusterurl
 ```
 
 The following are situations where parameter overrides would be useful:
@@ -34,7 +34,7 @@ case, the application would expose a parameter named `image`, whose value used i
 environment contains a placeholder value (e.g. `example/guestbook:replaceme`). The placeholder value
 would be determined externally (outside of Git) such as a build system. Then, as part of the build
 pipeline, the parameter value of the `image` would be continually updated to the freshly built image
-(e.g. `argocd app set guestbook -p guestbook=image=example/guestbook:abcd123`). A sync operation
+(e.g. `argocd app set guestbook -p image=example/guestbook:abcd123`). A sync operation
 would result in the application being redeployed with the new image.
 
 2. A repository of Helm manifests is already publicly available (e.g. https://github.com/helm/charts).
diff --git a/docs/user-guide/projects.md b/docs/user-guide/projects.md
index 4cb066a31..b4049aeb1 100644
--- a/docs/user-guide/projects.md
+++ b/docs/user-guide/projects.md
@@ -55,8 +55,8 @@ argocd proj remove-destination <PROJECT> <CLUSTER>,<NAMESPACE>
 ```
 
 Permitted destination K8s resource kinds are managed with the commands. Note that namespaced-scoped
-resources are restricted via a blacklist, whereas cluster-scoped resources are restricted via
-whitelist.
+resources are restricted via a deny list, whereas cluster-scoped resources are restricted via
+allow list.
 
 ```bash
 argocd proj allow-cluster-resource <PROJECT> <GROUP> <KIND>
diff --git a/go.mod b/go.mod
index 2c97810aa..6f41dcd3a 100644
--- a/go.mod
+++ b/go.mod
@@ -7,9 +7,9 @@ require (
 	github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
 	github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 // indirect
 	github.com/alicebob/miniredis v2.5.0+incompatible
-	github.com/argoproj/gitops-engine v0.1.3-0.20201027224148-eb76c93f0a2e
+	github.com/argoproj/gitops-engine v0.1.3-0.20201030194627-cfdefa46b20c
 	github.com/argoproj/pkg v0.2.0
-	github.com/bombsimon/logrusr v0.0.0-20200131103305-03a291ce59b4
+	github.com/bombsimon/logrusr v1.0.0
 	github.com/casbin/casbin v1.9.1
 	github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect
 	github.com/coreos/go-oidc v2.1.0+incompatible
diff --git a/go.sum b/go.sum
index 5b62c3850..4f234af38 100644
--- a/go.sum
+++ b/go.sum
@@ -84,8 +84,8 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
-github.com/argoproj/gitops-engine v0.1.3-0.20201027224148-eb76c93f0a2e h1:a/Kem2t1Zs9uEI9FDVDmNn3SneBuuaJvq+wyj1iuLwQ=
-github.com/argoproj/gitops-engine v0.1.3-0.20201027224148-eb76c93f0a2e/go.mod h1:OxXp8YaT73rw9gEBnGBWg55af80nkV/uIjWCbJu1Nw0=
+github.com/argoproj/gitops-engine v0.1.3-0.20201030194627-cfdefa46b20c h1:vc4gE1r2H0U8UtCdfSYdQJ4LtwbRtE2cy5Z3p0dgyys=
+github.com/argoproj/gitops-engine v0.1.3-0.20201030194627-cfdefa46b20c/go.mod h1:OxXp8YaT73rw9gEBnGBWg55af80nkV/uIjWCbJu1Nw0=
 github.com/argoproj/pkg v0.2.0 h1:ETgC600kr8WcAi3MEVY5sA1H7H/u1/IysYOobwsZ8No=
 github.com/argoproj/pkg v0.2.0/go.mod h1:F4TZgInLUEjzsWFB/BTJBsewoEy0ucnKSq6vmQiD/yc=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -101,7 +101,6 @@ github.com/aws/aws-sdk-go v1.28.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
 github.com/aws/aws-sdk-go v1.33.16/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
 github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@@ -111,8 +110,8 @@ github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb
 github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs=
 github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
 github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
-github.com/bombsimon/logrusr v0.0.0-20200131103305-03a291ce59b4 h1:Myj4mlaLi25AoncNPb8uf+Riru0Mp4xw7S9RUxBCYxk=
-github.com/bombsimon/logrusr v0.0.0-20200131103305-03a291ce59b4/go.mod h1:Jq0nHtvxabKE5EMwAAdgTaz7dfWE8C4i11NOltxGQpc=
+github.com/bombsimon/logrusr v1.0.0 h1:CTCkURYAt5nhCCnKH9eLShYayj2/8Kn/4Qg3QfiU+Ro=
+github.com/bombsimon/logrusr v1.0.0/go.mod h1:Jq0nHtvxabKE5EMwAAdgTaz7dfWE8C4i11NOltxGQpc=
 github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E=
 github.com/casbin/casbin v1.9.1 h1:ucjbS5zTrmSLtH4XogqOG920Poe6QatdXtz1FEbApeM=
 github.com/casbin/casbin v1.9.1/go.mod h1:z8uPsfBJGUsnkagrt3G8QvjgTKFMBJ32UP8HpZllfog=
@@ -215,7 +214,6 @@ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
-github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -236,7 +234,6 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
-github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY=
 github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
 github.com/go-logr/logr v0.2.1 h1:fV3MLmabKIZ383XifUjFSwcoGee0v9qgPp8wy5svibE=
 github.com/go-logr/logr v0.2.1/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
@@ -296,7 +293,6 @@ github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85n
 github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
 github.com/go-redis/cache/v8 v8.0.0-beta.11 h1:6QdpDYemSFQ2yrDkDkEofHpXid6Mm3UctqaVUzxGdn8=
 github.com/go-redis/cache/v8 v8.0.0-beta.11/go.mod h1:4wxD/neK+Uw+SteOR+AXtlyQYMBlI/D1u7UahfDCBAI=
-github.com/go-redis/redis/v8 v8.0.0-beta.2 h1:9S28J9QMBotgI3tGgXbX1Wk9i8QYC3Orw4bTLoPrQeI=
 github.com/go-redis/redis/v8 v8.0.0-beta.2/go.mod h1:o1M7JtsgfDYyv3o+gBn/jJ1LkqpnCrmil7PSppZGBak=
 github.com/go-redis/redis/v8 v8.0.0-beta.9 h1:gcRNXZvs4PFi/mptho1D3hXant8JsbQKQxQ+0W3QGPw=
 github.com/go-redis/redis/v8 v8.0.0-beta.9/go.mod h1:CCWlPAL3uftOyyBOGpJ+A0ps2WpG3+hqo14fVs5aQ6A=
@@ -317,9 +313,7 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
@@ -351,11 +345,9 @@ github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
 github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-jsonnet v0.16.0 h1:Nb4EEOp+rdeGGyB1rQ5eisgSAqrTnhf9ip+X6lzZbY0=
 github.com/google/go-jsonnet v0.16.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
-github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -437,13 +429,11 @@ github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqj
 github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
 github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
 github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -475,14 +465,11 @@ github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8 h1:A6SLdFpRzUUF5v
 github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8/go.mod h1:UtpLyb/EupVKXF/N0b4NRe1DNg+QYJsnsHQ038romhM=
 github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
 github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
@@ -567,26 +554,22 @@ github.com/pquerna/cachecontrol v0.0.0-20180306154005-525d0eb5f91d h1:7gXyC293Ls
 github.com/pquerna/cachecontrol v0.0.0-20180306154005-525d0eb5f91d/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
-github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
 github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
 github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
 github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
@@ -614,7 +597,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5I
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
-github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
@@ -697,7 +679,6 @@ go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opentelemetry.io/otel v0.5.0 h1:tdIR1veg/z+VRJaw/6SIxz+QX3l+m+BDleYLTs+GC1g=
 go.opentelemetry.io/otel v0.5.0/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek=
 go.opentelemetry.io/otel v0.11.0 h1:IN2tzQa9Gc4ZVKnTaMbPVcHjvzOdg5n9QfnmlqiET7E=
 go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0=
@@ -728,7 +709,6 @@ golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk=
 golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
@@ -751,7 +731,6 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG
 golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -778,7 +757,6 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
 golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -786,13 +764,11 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY=
 golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
 golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -801,7 +777,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -833,7 +808,6 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -849,19 +823,16 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
 golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8=
 golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -896,7 +867,6 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a h1:gILuVKC+ZPD6g/tj6zBOdnOH1ZHI0zZ86+KLMogc6/s=
 golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
 golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@@ -905,11 +875,9 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
-gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw=
 gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
 gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
 gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
-gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e h1:jRyg0XfpwWlhEV8mDfdNGBeSJM2fuyh9Yjrnd8kF2Ts=
 gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
 gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
@@ -921,7 +889,6 @@ google.golang.org/api v0.15.1-0.20200106000736-b8fc810ca6b5/go.mod h1:iLdEw5Ide6
 google.golang.org/api v0.15.1/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -931,13 +898,11 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
 google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
 google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o1N2tjo8UUSlhQgAD52FVkwxnWgM8=
 google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
@@ -1019,7 +984,6 @@ k8s.io/component-base v0.19.2 h1:jW5Y9RcZTb79liEhW3XDVTW7MuvEGP0tQZnfSX6/+gs=
 k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo=
 k8s.io/cri-api v0.19.2/go.mod h1:UN/iU9Ua0iYdDREBXNE9vqCJ7MIh/FW3VIL0d8pw7Fw=
 k8s.io/csi-translation-lib v0.19.2/go.mod h1:9V3DYlJ6reyKD+/S/JrFyk/j0N1i8FCQYukch0JbNew=
-k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac h1:sAvhNk5RRuc6FNYGqe7Ygz3PSo/2wGWbulskmzRX8Vs=
 k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14 h1:t4L10Qfx/p7ASH3gXCdIUtPbbIuegCoUJf3TMSFekjw=
 k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
diff --git a/hack/tool-versions.sh b/hack/tool-versions.sh
index ef64e8881..97a8c1e8a 100644
--- a/hack/tool-versions.sh
+++ b/hack/tool-versions.sh
@@ -1,8 +1,8 @@
 #!/bin/sh
 # The checksum of this file is used as cache key in our integration toolchain
 # 
-helm2_version=2.15.2
-helm3_version=3.3.1
+helm2_version=2.17.0
+helm3_version=3.4.0
 jq_version=1.6
 ksonnet_version=0.13.1
 kubectl_version=1.17.8
diff --git a/reposerver/apiclient/repository.pb.go b/reposerver/apiclient/repository.pb.go
index 210daf73f..804d7d39f 100644
--- a/reposerver/apiclient/repository.pb.go
+++ b/reposerver/apiclient/repository.pb.go
@@ -628,7 +628,9 @@ type RepoServerRevisionMetadataRequest struct {
 	// the repo
 	Repo *v1alpha1.Repository `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
 	// the revision within the repo
-	Revision             string   `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"`
+	Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"`
+	// whether to check signature on revision
+	CheckSignature       bool     `protobuf:"varint,3,opt,name=checkSignature,proto3" json:"checkSignature,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
 	XXX_sizecache        int32    `json:"-"`
@@ -681,6 +683,13 @@ func (m *RepoServerRevisionMetadataRequest) GetRevision() string {
 	return ""
 }
 
+func (m *RepoServerRevisionMetadataRequest) GetCheckSignature() bool {
+	if m != nil {
+		return m.CheckSignature
+	}
+	return false
+}
+
 // KsonnetAppSpec contains Ksonnet app response
 // This roughly reflects: ksonnet/ksonnet/metadata/app/schema.go
 type KsonnetAppSpec struct {
@@ -1218,87 +1227,88 @@ func init() {
 }
 
 var fileDescriptor_dd8723cfcc820480 = []byte{
-	// 1280 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x5b, 0x6f, 0x1b, 0xc5,
-	0x17, 0xcf, 0xda, 0xce, 0xc5, 0xc7, 0x6d, 0xe3, 0x4c, 0xfb, 0xef, 0x7f, 0x31, 0xa9, 0x15, 0x56,
-	0x80, 0x02, 0xa5, 0x36, 0x0d, 0x15, 0x54, 0xad, 0x54, 0x29, 0xf4, 0x8a, 0x92, 0xaa, 0xe9, 0x06,
-	0x2a, 0x71, 0x91, 0xaa, 0xc9, 0xfa, 0x64, 0x3d, 0x78, 0xbd, 0x3b, 0xec, 0x8c, 0x8d, 0xdc, 0x2f,
-	0x00, 0xef, 0x88, 0x17, 0xbe, 0x00, 0xef, 0x48, 0x3c, 0xf0, 0x8e, 0x10, 0x8f, 0x7c, 0x04, 0xd4,
-	0x4f, 0x82, 0x66, 0xf6, 0x36, 0x5e, 0x3b, 0x79, 0x71, 0x2f, 0x2f, 0xc9, 0xcc, 0x99, 0x73, 0x9b,
-	0x33, 0xbf, 0xf9, 0xcd, 0xf1, 0xc2, 0xbb, 0x31, 0xf2, 0x48, 0x60, 0x3c, 0xc6, 0xb8, 0xab, 0x87,
-	0x4c, 0x46, 0xf1, 0xc4, 0x18, 0x76, 0x78, 0x1c, 0xc9, 0x88, 0x40, 0x21, 0x69, 0x5d, 0xf0, 0x23,
-	0x3f, 0xd2, 0xe2, 0xae, 0x1a, 0x25, 0x1a, 0xad, 0x4d, 0x3f, 0x8a, 0xfc, 0x00, 0xbb, 0x94, 0xb3,
-	0x2e, 0x0d, 0xc3, 0x48, 0x52, 0xc9, 0xa2, 0x50, 0xa4, 0xab, 0xce, 0xe0, 0xba, 0xe8, 0xb0, 0x48,
-	0xaf, 0x7a, 0x51, 0x8c, 0xdd, 0xf1, 0xd5, 0xae, 0x8f, 0x21, 0xc6, 0x54, 0x62, 0x2f, 0xd5, 0xf9,
-	0xcc, 0x67, 0xb2, 0x3f, 0x3a, 0xea, 0x78, 0xd1, 0xb0, 0x4b, 0x63, 0x1d, 0xe2, 0x5b, 0x3d, 0xb8,
-	0xe2, 0xf5, 0xba, 0x7c, 0xe0, 0x2b, 0x63, 0xd1, 0xa5, 0x9c, 0x07, 0xcc, 0xd3, 0xce, 0xbb, 0xe3,
-	0xab, 0x34, 0xe0, 0x7d, 0x3a, 0xe3, 0xca, 0xf9, 0x7d, 0x05, 0xd6, 0x1f, 0xd2, 0x90, 0x1d, 0xa3,
-	0x90, 0x2e, 0x7e, 0x37, 0x42, 0x21, 0xc9, 0x97, 0x50, 0x53, 0x9b, 0xb0, 0xad, 0x2d, 0x6b, 0xbb,
-	0xb1, 0x73, 0xb7, 0x53, 0x44, 0xeb, 0x64, 0xd1, 0xf4, 0xe0, 0xa9, 0xd7, 0xeb, 0xf0, 0x81, 0xdf,
-	0x51, 0xd1, 0x3a, 0x46, 0xb4, 0x4e, 0x16, 0xad, 0xe3, 0xe6, 0xb5, 0x70, 0xb5, 0x4b, 0xd2, 0x82,
-	0xb5, 0x18, 0xc7, 0x4c, 0xb0, 0x28, 0xb4, 0x2b, 0x5b, 0xd6, 0x76, 0xdd, 0xcd, 0xe7, 0xc4, 0x86,
-	0xd5, 0x30, 0xba, 0x4d, 0xbd, 0x3e, 0xda, 0xd5, 0x2d, 0x6b, 0x7b, 0xcd, 0xcd, 0xa6, 0x64, 0x0b,
-	0x1a, 0x94, 0xf3, 0x7d, 0x7a, 0x84, 0xc1, 0x1e, 0x4e, 0xec, 0x9a, 0x36, 0x34, 0x45, 0xe4, 0x6d,
-	0x38, 0x9b, 0x4d, 0x9f, 0xd0, 0x60, 0x84, 0xf6, 0xb2, 0xd6, 0x99, 0x16, 0x92, 0x4d, 0xa8, 0x87,
-	0x74, 0x88, 0x82, 0x53, 0x0f, 0xed, 0x35, 0xad, 0x51, 0x08, 0xc8, 0x33, 0xd8, 0x30, 0x36, 0x71,
-	0x18, 0x8d, 0x62, 0x0f, 0x6d, 0xd0, 0x35, 0xd8, 0x5f, 0xa0, 0x06, 0xbb, 0x65, 0x9f, 0xee, 0x6c,
-	0x18, 0xf2, 0x35, 0x2c, 0x6b, 0xdc, 0xd8, 0x8d, 0xad, 0xea, 0x8b, 0xab, 0x79, 0xe2, 0x93, 0x0c,
-	0x60, 0x95, 0x07, 0x23, 0x9f, 0x85, 0xc2, 0x3e, 0xa3, 0xdd, 0x3f, 0x5e, 0xc0, 0xfd, 0xed, 0x28,
-	0x3c, 0x66, 0xfe, 0x43, 0x1a, 0x52, 0x1f, 0x87, 0x18, 0xca, 0x03, 0xed, 0xd9, 0xcd, 0x22, 0x90,
-	0xef, 0xa1, 0x39, 0x18, 0x09, 0x19, 0x0d, 0xd9, 0x33, 0x7c, 0xc4, 0x35, 0xb2, 0xed, 0xb3, 0xba,
-	0x88, 0x7b, 0x0b, 0x44, 0xdd, 0x2b, 0xb9, 0x74, 0x67, 0x82, 0x28, 0x90, 0x0c, 0x46, 0x47, 0xf8,
-	0x04, 0x63, 0x8d, 0xae, 0x73, 0x09, 0x48, 0x0c, 0x51, 0x02, 0x23, 0x96, 0xce, 0x84, 0xbd, 0xbe,
-	0x55, 0x4d, 0x60, 0x94, 0x8b, 0xc8, 0x36, 0xac, 0x8f, 0x31, 0x66, 0xc7, 0x93, 0x43, 0xe6, 0x87,
-	0x54, 0x8e, 0x62, 0xb4, 0x9b, 0x1a, 0x8a, 0x65, 0xb1, 0xf3, 0x97, 0x05, 0xcd, 0xe2, 0xde, 0x08,
-	0x1e, 0x85, 0x42, 0xe3, 0x6b, 0x98, 0xca, 0x84, 0x6d, 0x69, 0xf7, 0x85, 0x60, 0x1a, 0x7d, 0x95,
-	0x32, 0xfa, 0x2e, 0xc2, 0x4a, 0xc2, 0x2e, 0x1a, 0xfc, 0x75, 0x37, 0x9d, 0x4d, 0xdd, 0x98, 0x5a,
-	0xe9, 0xc6, 0xb4, 0x01, 0x84, 0xc6, 0xcf, 0xe7, 0x13, 0x8e, 0xf6, 0x8a, 0x5e, 0x35, 0x24, 0xc4,
-	0x81, 0x33, 0x49, 0xde, 0x2e, 0x8a, 0x51, 0x20, 0xed, 0x55, 0xad, 0x31, 0x25, 0x73, 0x02, 0x58,
-	0xdf, 0x67, 0x6a, 0x0f, 0xc7, 0xe2, 0xe5, 0xdf, 0x7f, 0xe7, 0x63, 0xa8, 0xa9, 0x48, 0x6a, 0x57,
-	0x47, 0x31, 0x0d, 0xbd, 0x3e, 0x66, 0x85, 0xca, 0xe7, 0x84, 0x40, 0x4d, 0x52, 0x5f, 0xd8, 0x15,
-	0x2d, 0xd7, 0x63, 0xe7, 0x47, 0x2b, 0x49, 0x73, 0x97, 0x73, 0xf1, 0x7a, 0x69, 0xca, 0x19, 0xc1,
-	0xea, 0x2e, 0xe7, 0x2a, 0x19, 0x72, 0x15, 0x6a, 0x94, 0xf3, 0x64, 0x07, 0x8d, 0x9d, 0x4b, 0x1d,
-	0xe3, 0x31, 0x48, 0x55, 0xd4, 0x7f, 0x71, 0x37, 0x94, 0xca, 0xb3, 0x52, 0x6d, 0x7d, 0x02, 0xf5,
-	0x5c, 0x44, 0x9a, 0x50, 0x1d, 0xe0, 0x44, 0x6f, 0xa0, 0xee, 0xaa, 0x21, 0xb9, 0x00, 0xcb, 0x63,
-	0xcd, 0x5f, 0x49, 0xd4, 0x64, 0x72, 0xa3, 0x72, 0xdd, 0x72, 0x7e, 0xab, 0xc2, 0x1b, 0x2a, 0xcf,
-	0x43, 0x0d, 0x8b, 0x5d, 0xce, 0xef, 0xa0, 0xa4, 0x2c, 0x10, 0x8f, 0x47, 0x18, 0x4f, 0x5e, 0x66,
-	0x2d, 0x7a, 0xb0, 0x92, 0x40, 0x4a, 0xe7, 0xf4, 0xa2, 0xb9, 0x30, 0xf5, 0x5d, 0x10, 0x60, 0xf5,
-	0x25, 0x10, 0xe0, 0x3c, 0x4e, 0xaa, 0xbd, 0x02, 0x4e, 0x72, 0x7e, 0xa8, 0xc0, 0x45, 0x95, 0x4e,
-	0x71, 0x5c, 0x39, 0x57, 0x28, 0x94, 0xab, 0x5b, 0x9b, 0x1c, 0xbe, 0x1e, 0x93, 0x6b, 0xb0, 0x3a,
-	0x10, 0x51, 0x18, 0xa2, 0x4c, 0x6b, 0xdd, 0x32, 0x21, 0xb5, 0x97, 0x2c, 0xed, 0x72, 0x7e, 0xc8,
-	0xd1, 0x73, 0x33, 0x55, 0x72, 0x19, 0x6a, 0x7d, 0x0c, 0x86, 0x9a, 0x37, 0x1a, 0x3b, 0xff, 0x37,
-	0x4d, 0x1e, 0x60, 0x30, 0xcc, 0xf4, 0xb5, 0x12, 0xb9, 0x01, 0xf5, 0x3c, 0xcb, 0xb4, 0x06, 0x9b,
-	0x53, 0x41, 0xb2, 0xc5, 0xcc, 0xac, 0x50, 0x57, 0xb6, 0x3d, 0x16, 0xa3, 0xa7, 0x14, 0xf5, 0x03,
-	0x5b, 0xb2, 0xbd, 0x93, 0x2d, 0xe6, 0xb6, 0xb9, 0xba, 0xf3, 0x8b, 0x05, 0x6f, 0x15, 0xf0, 0x75,
-	0xd3, 0xcb, 0xf4, 0x10, 0x25, 0xed, 0x51, 0x49, 0x5f, 0xf3, 0x95, 0xfe, 0xb3, 0x02, 0xe7, 0xa6,
-	0xab, 0xab, 0x8e, 0x47, 0x71, 0x73, 0x76, 0x3c, 0x6a, 0x4c, 0x0e, 0xe0, 0x0c, 0x86, 0x63, 0x16,
-	0x47, 0xa1, 0x7a, 0xf8, 0x32, 0xa8, 0x7e, 0x70, 0xf2, 0x19, 0x75, 0xee, 0x1a, 0xea, 0x09, 0x0b,
-	0x4c, 0x79, 0x20, 0x03, 0x00, 0x4e, 0x63, 0x3a, 0x44, 0x89, 0xb1, 0x82, 0x64, 0x75, 0x51, 0x48,
-	0x26, 0xe1, 0x0f, 0x32, 0x9f, 0xae, 0xe1, 0xbe, 0xf5, 0x14, 0x36, 0x66, 0xf2, 0x99, 0x43, 0x41,
-	0xd7, 0x4c, 0x0a, 0x6a, 0xec, 0xb4, 0xe7, 0x6c, 0xcf, 0x70, 0x63, 0x52, 0xd4, 0x1f, 0x15, 0x68,
-	0x18, 0x88, 0x9b, 0x5b, 0xc3, 0x36, 0x80, 0x36, 0xb8, 0xc7, 0x02, 0x4c, 0x2a, 0x58, 0x77, 0x0d,
-	0x09, 0xe9, 0xcf, 0xa9, 0xc8, 0x83, 0x05, 0x2a, 0xa2, 0xf2, 0x99, 0x5b, 0x0e, 0xf5, 0xe0, 0xea,
-	0xb8, 0x22, 0xed, 0x15, 0xd3, 0x19, 0x91, 0x70, 0xee, 0x98, 0x05, 0x78, 0x50, 0x64, 0xb1, 0xa2,
-	0xb3, 0xd8, 0x5f, 0x30, 0x8b, 0x7b, 0xa6, 0x53, 0xb7, 0x14, 0xc3, 0x79, 0x1f, 0x9a, 0xe5, 0xab,
-	0xa7, 0x32, 0x64, 0x43, 0xea, 0xe7, 0x75, 0x4a, 0x67, 0xce, 0xcf, 0x16, 0x90, 0xd9, 0x93, 0x38,
-	0xa9, 0xdc, 0x83, 0xeb, 0x22, 0xeb, 0x89, 0x12, 0xdc, 0x1b, 0x12, 0xb2, 0x07, 0x8d, 0x1e, 0x0a,
-	0xc9, 0x42, 0x9d, 0x70, 0x4a, 0x08, 0xef, 0x9d, 0x7e, 0xe4, 0x77, 0x0a, 0x03, 0xd7, 0xb4, 0x76,
-	0xbe, 0x80, 0x4b, 0xa7, 0x6a, 0x1b, 0x3d, 0x8e, 0x35, 0xd5, 0xe3, 0x9c, 0xda, 0x19, 0x39, 0x04,
-	0x9a, 0x65, 0x66, 0x71, 0x42, 0xd8, 0x50, 0x35, 0xbd, 0xdd, 0xa7, 0xb1, 0x7c, 0x15, 0x7d, 0xcb,
-	0x4d, 0xa8, 0xe7, 0xf1, 0xe6, 0x16, 0xba, 0x05, 0x6b, 0xe3, 0xac, 0xb1, 0x4c, 0x1a, 0x97, 0x7c,
-	0xee, 0xec, 0x02, 0x31, 0x93, 0x4d, 0x1f, 0x80, 0xcb, 0xb0, 0xcc, 0x24, 0x0e, 0xb3, 0xee, 0xe1,
-	0x7f, 0x65, 0xde, 0xd6, 0xea, 0x6e, 0xa2, 0xb3, 0xf3, 0x6b, 0x0d, 0x36, 0x0a, 0xfa, 0x54, 0x7f,
-	0x99, 0x87, 0xe4, 0x11, 0x34, 0xef, 0xa7, 0xbf, 0xe7, 0xb2, 0x5e, 0x94, 0xbc, 0x69, 0xfa, 0x29,
-	0xfd, 0xb2, 0x6b, 0x6d, 0xce, 0x5f, 0x4c, 0x32, 0x72, 0x96, 0xc8, 0x4d, 0x58, 0xcb, 0x9a, 0xc1,
-	0x69, 0x47, 0xa5, 0x16, 0xb1, 0xd5, 0x34, 0x17, 0xd5, 0x82, 0xb3, 0x44, 0x6e, 0x25, 0xc6, 0xaa,
-	0xbd, 0x99, 0x35, 0x36, 0x1a, 0xb7, 0xd6, 0xf9, 0x39, 0x8d, 0x92, 0xb3, 0x44, 0xbe, 0x81, 0xb3,
-	0xf7, 0x35, 0x75, 0xa6, 0x4f, 0x25, 0x79, 0x67, 0x3a, 0xc8, 0x09, 0xbd, 0x4f, 0xcb, 0x29, 0xab,
-	0xcd, 0xbe, 0xb6, 0xce, 0x12, 0xf9, 0xc9, 0x82, 0xf3, 0xf7, 0x51, 0x96, 0x5f, 0x1e, 0x72, 0x65,
-	0x7e, 0x90, 0x13, 0x5e, 0xa8, 0xd6, 0xde, 0x42, 0xa8, 0x9a, 0xf6, 0xe9, 0x2c, 0x91, 0x03, 0xbd,
-	0xe7, 0x02, 0x1d, 0xe4, 0xd2, 0x5c, 0x18, 0xe4, 0xa5, 0x6b, 0x9f, 0xb4, 0x9c, 0xed, 0xf3, 0xd3,
-	0x5b, 0x7f, 0x3f, 0x6f, 0x5b, 0xff, 0x3c, 0x6f, 0x5b, 0xff, 0x3e, 0x6f, 0x5b, 0x5f, 0x7d, 0x78,
-	0xda, 0x97, 0x02, 0xe3, 0x8b, 0x06, 0xe5, 0xcc, 0x0b, 0x18, 0x86, 0xf2, 0x68, 0x45, 0x7f, 0x17,
-	0xf8, 0xe8, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x61, 0xb4, 0xe7, 0xb1, 0xf0, 0x10, 0x00, 0x00,
+	// 1292 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x5b, 0x6f, 0x1b, 0x45,
+	0x14, 0xce, 0xda, 0xce, 0xc5, 0xc7, 0x6d, 0xe2, 0x4c, 0x4b, 0x59, 0x4c, 0x6a, 0x85, 0x15, 0x54,
+	0x81, 0x52, 0x9b, 0x86, 0x0a, 0xaa, 0x56, 0xaa, 0x14, 0x7a, 0x45, 0x49, 0xd5, 0x74, 0x03, 0x95,
+	0xb8, 0x48, 0xd5, 0x64, 0x7d, 0xb2, 0x1e, 0xbc, 0xde, 0x1d, 0x76, 0xc6, 0x46, 0xe9, 0x1f, 0x80,
+	0x77, 0xc4, 0x6f, 0xe0, 0x1d, 0x89, 0x07, 0x5e, 0x11, 0x42, 0x3c, 0xf2, 0x13, 0x50, 0x7f, 0x09,
+	0x9a, 0xd9, 0xdb, 0x78, 0xed, 0xe4, 0xc5, 0xbd, 0xbc, 0x24, 0x33, 0x67, 0xce, 0x6d, 0xce, 0x7c,
+	0xf3, 0xcd, 0xf1, 0xc2, 0xa5, 0x18, 0x79, 0x24, 0x30, 0x1e, 0x63, 0xdc, 0xd5, 0x43, 0x26, 0xa3,
+	0xf8, 0xd8, 0x18, 0x76, 0x78, 0x1c, 0xc9, 0x88, 0x40, 0x21, 0x69, 0x9d, 0xf7, 0x23, 0x3f, 0xd2,
+	0xe2, 0xae, 0x1a, 0x25, 0x1a, 0xad, 0x0d, 0x3f, 0x8a, 0xfc, 0x00, 0xbb, 0x94, 0xb3, 0x2e, 0x0d,
+	0xc3, 0x48, 0x52, 0xc9, 0xa2, 0x50, 0xa4, 0xab, 0xce, 0xe0, 0xba, 0xe8, 0xb0, 0x48, 0xaf, 0x7a,
+	0x51, 0x8c, 0xdd, 0xf1, 0xd5, 0xae, 0x8f, 0x21, 0xc6, 0x54, 0x62, 0x2f, 0xd5, 0xf9, 0xdc, 0x67,
+	0xb2, 0x3f, 0x3a, 0xec, 0x78, 0xd1, 0xb0, 0x4b, 0x63, 0x1d, 0xe2, 0x3b, 0x3d, 0xb8, 0xe2, 0xf5,
+	0xba, 0x7c, 0xe0, 0x2b, 0x63, 0xd1, 0xa5, 0x9c, 0x07, 0xcc, 0xd3, 0xce, 0xbb, 0xe3, 0xab, 0x34,
+	0xe0, 0x7d, 0x3a, 0xe5, 0xca, 0xf9, 0x7d, 0x09, 0xd6, 0x1e, 0xd2, 0x90, 0x1d, 0xa1, 0x90, 0x2e,
+	0x7e, 0x3f, 0x42, 0x21, 0xc9, 0x57, 0x50, 0x53, 0x9b, 0xb0, 0xad, 0x4d, 0x6b, 0xab, 0xb1, 0x7d,
+	0xb7, 0x53, 0x44, 0xeb, 0x64, 0xd1, 0xf4, 0xe0, 0xa9, 0xd7, 0xeb, 0xf0, 0x81, 0xdf, 0x51, 0xd1,
+	0x3a, 0x46, 0xb4, 0x4e, 0x16, 0xad, 0xe3, 0xe6, 0xb5, 0x70, 0xb5, 0x4b, 0xd2, 0x82, 0x95, 0x18,
+	0xc7, 0x4c, 0xb0, 0x28, 0xb4, 0x2b, 0x9b, 0xd6, 0x56, 0xdd, 0xcd, 0xe7, 0xc4, 0x86, 0xe5, 0x30,
+	0xba, 0x4d, 0xbd, 0x3e, 0xda, 0xd5, 0x4d, 0x6b, 0x6b, 0xc5, 0xcd, 0xa6, 0x64, 0x13, 0x1a, 0x94,
+	0xf3, 0x3d, 0x7a, 0x88, 0xc1, 0x2e, 0x1e, 0xdb, 0x35, 0x6d, 0x68, 0x8a, 0xc8, 0xbb, 0x70, 0x36,
+	0x9b, 0x3e, 0xa1, 0xc1, 0x08, 0xed, 0x45, 0xad, 0x33, 0x29, 0x24, 0x1b, 0x50, 0x0f, 0xe9, 0x10,
+	0x05, 0xa7, 0x1e, 0xda, 0x2b, 0x5a, 0xa3, 0x10, 0x90, 0x67, 0xb0, 0x6e, 0x6c, 0xe2, 0x20, 0x1a,
+	0xc5, 0x1e, 0xda, 0xa0, 0x6b, 0xb0, 0x37, 0x47, 0x0d, 0x76, 0xca, 0x3e, 0xdd, 0xe9, 0x30, 0xe4,
+	0x1b, 0x58, 0xd4, 0xb8, 0xb1, 0x1b, 0x9b, 0xd5, 0x17, 0x57, 0xf3, 0xc4, 0x27, 0x19, 0xc0, 0x32,
+	0x0f, 0x46, 0x3e, 0x0b, 0x85, 0x7d, 0x46, 0xbb, 0x7f, 0x3c, 0x87, 0xfb, 0xdb, 0x51, 0x78, 0xc4,
+	0xfc, 0x87, 0x34, 0xa4, 0x3e, 0x0e, 0x31, 0x94, 0xfb, 0xda, 0xb3, 0x9b, 0x45, 0x20, 0x3f, 0x40,
+	0x73, 0x30, 0x12, 0x32, 0x1a, 0xb2, 0x67, 0xf8, 0x88, 0x6b, 0x64, 0xdb, 0x67, 0x75, 0x11, 0x77,
+	0xe7, 0x88, 0xba, 0x5b, 0x72, 0xe9, 0x4e, 0x05, 0x51, 0x20, 0x19, 0x8c, 0x0e, 0xf1, 0x09, 0xc6,
+	0x1a, 0x5d, 0xab, 0x09, 0x48, 0x0c, 0x51, 0x02, 0x23, 0x96, 0xce, 0x84, 0xbd, 0xb6, 0x59, 0x4d,
+	0x60, 0x94, 0x8b, 0xc8, 0x16, 0xac, 0x8d, 0x31, 0x66, 0x47, 0xc7, 0x07, 0xcc, 0x0f, 0xa9, 0x1c,
+	0xc5, 0x68, 0x37, 0x35, 0x14, 0xcb, 0x62, 0xe7, 0x6f, 0x0b, 0x9a, 0xc5, 0xbd, 0x11, 0x3c, 0x0a,
+	0x85, 0xc6, 0xd7, 0x30, 0x95, 0x09, 0xdb, 0xd2, 0xee, 0x0b, 0xc1, 0x24, 0xfa, 0x2a, 0x65, 0xf4,
+	0x5d, 0x80, 0xa5, 0x84, 0x5d, 0x34, 0xf8, 0xeb, 0x6e, 0x3a, 0x9b, 0xb8, 0x31, 0xb5, 0xd2, 0x8d,
+	0x69, 0x03, 0x08, 0x8d, 0x9f, 0x2f, 0x8e, 0x39, 0xda, 0x4b, 0x7a, 0xd5, 0x90, 0x10, 0x07, 0xce,
+	0x24, 0x79, 0xbb, 0x28, 0x46, 0x81, 0xb4, 0x97, 0xb5, 0xc6, 0x84, 0xcc, 0x09, 0x60, 0x6d, 0x8f,
+	0xa9, 0x3d, 0x1c, 0x89, 0x97, 0x7f, 0xff, 0x9d, 0x4f, 0xa0, 0xa6, 0x22, 0xa9, 0x5d, 0x1d, 0xc6,
+	0x34, 0xf4, 0xfa, 0x98, 0x15, 0x2a, 0x9f, 0x13, 0x02, 0x35, 0x49, 0x7d, 0x61, 0x57, 0xb4, 0x5c,
+	0x8f, 0x9d, 0x9f, 0xac, 0x24, 0xcd, 0x1d, 0xce, 0xc5, 0xeb, 0xa5, 0x29, 0x67, 0x04, 0xcb, 0x3b,
+	0x9c, 0xab, 0x64, 0xc8, 0x55, 0xa8, 0x51, 0xce, 0x93, 0x1d, 0x34, 0xb6, 0x2f, 0x76, 0x8c, 0xc7,
+	0x20, 0x55, 0x51, 0xff, 0xc5, 0xdd, 0x50, 0x2a, 0xcf, 0x4a, 0xb5, 0xf5, 0x29, 0xd4, 0x73, 0x11,
+	0x69, 0x42, 0x75, 0x80, 0xc7, 0x7a, 0x03, 0x75, 0x57, 0x0d, 0xc9, 0x79, 0x58, 0x1c, 0x6b, 0xfe,
+	0x4a, 0xa2, 0x26, 0x93, 0x1b, 0x95, 0xeb, 0x96, 0xf3, 0x5b, 0x15, 0xde, 0x52, 0x79, 0x1e, 0x68,
+	0x58, 0xec, 0x70, 0x7e, 0x07, 0x25, 0x65, 0x81, 0x78, 0x3c, 0xc2, 0xf8, 0xf8, 0x65, 0xd6, 0xa2,
+	0x07, 0x4b, 0x09, 0xa4, 0x74, 0x4e, 0x2f, 0x9a, 0x0b, 0x53, 0xdf, 0x05, 0x01, 0x56, 0x5f, 0x02,
+	0x01, 0xce, 0xe2, 0xa4, 0xda, 0x2b, 0xe0, 0x24, 0xe7, 0xc7, 0x0a, 0x5c, 0x50, 0xe9, 0x14, 0xc7,
+	0x95, 0x73, 0x85, 0x42, 0xb9, 0xba, 0xb5, 0xc9, 0xe1, 0xeb, 0x31, 0xb9, 0x06, 0xcb, 0x03, 0x11,
+	0x85, 0x21, 0xca, 0xb4, 0xd6, 0x2d, 0x13, 0x52, 0xbb, 0xc9, 0xd2, 0x0e, 0xe7, 0x07, 0x1c, 0x3d,
+	0x37, 0x53, 0x25, 0x97, 0xa1, 0xd6, 0xc7, 0x60, 0xa8, 0x79, 0xa3, 0xb1, 0xfd, 0xa6, 0x69, 0xf2,
+	0x00, 0x83, 0x61, 0xa6, 0xaf, 0x95, 0xc8, 0x0d, 0xa8, 0xe7, 0x59, 0xa6, 0x35, 0xd8, 0x98, 0x08,
+	0x92, 0x2d, 0x66, 0x66, 0x85, 0xba, 0xb2, 0xed, 0xb1, 0x18, 0x3d, 0xa5, 0xa8, 0x1f, 0xd8, 0x92,
+	0xed, 0x9d, 0x6c, 0x31, 0xb7, 0xcd, 0xd5, 0x9d, 0x3f, 0x2d, 0x78, 0xa7, 0x80, 0xaf, 0x9b, 0x5e,
+	0xa6, 0x87, 0x28, 0x69, 0x8f, 0x4a, 0xfa, 0x9a, 0x3b, 0x8f, 0x4b, 0xb0, 0xea, 0xf5, 0xd1, 0x1b,
+	0x14, 0xac, 0x9f, 0x34, 0x20, 0x25, 0xa9, 0xf3, 0x57, 0x05, 0x56, 0x27, 0x4f, 0x41, 0x1d, 0xa3,
+	0xe2, 0xf0, 0xec, 0x18, 0xd5, 0x98, 0xec, 0xc3, 0x19, 0x0c, 0xc7, 0x2c, 0x8e, 0x42, 0xf5, 0x40,
+	0x66, 0x90, 0xfe, 0xf0, 0xe4, 0xb3, 0xec, 0xdc, 0x35, 0xd4, 0x13, 0xb6, 0x98, 0xf0, 0x40, 0x06,
+	0x00, 0x9c, 0xc6, 0x74, 0x88, 0x12, 0x63, 0x05, 0xdd, 0xea, 0xbc, 0xd0, 0x4d, 0xc2, 0xef, 0x67,
+	0x3e, 0x5d, 0xc3, 0x7d, 0xeb, 0x29, 0xac, 0x4f, 0xe5, 0x33, 0x83, 0xaa, 0xae, 0x99, 0x54, 0xd5,
+	0xd8, 0x6e, 0xcf, 0xd8, 0x9e, 0xe1, 0xc6, 0xa4, 0xb2, 0x3f, 0x2a, 0xd0, 0x30, 0x90, 0x39, 0xb3,
+	0x86, 0x6d, 0x00, 0x6d, 0x70, 0x8f, 0x05, 0x98, 0x54, 0xb0, 0xee, 0x1a, 0x12, 0xd2, 0x9f, 0x51,
+	0x91, 0x07, 0x73, 0x54, 0x44, 0xe5, 0x33, 0xb3, 0x1c, 0xea, 0x61, 0xd6, 0x71, 0x45, 0xda, 0x53,
+	0xa6, 0x33, 0x22, 0x61, 0xf5, 0x88, 0x05, 0xb8, 0x5f, 0x64, 0xb1, 0xa4, 0xb3, 0xd8, 0x9b, 0x33,
+	0x8b, 0x7b, 0xa6, 0x53, 0xb7, 0x14, 0xc3, 0xf9, 0x00, 0x9a, 0xe5, 0x2b, 0xaa, 0x32, 0x64, 0x43,
+	0xea, 0xe7, 0x75, 0x4a, 0x67, 0xce, 0x2f, 0x16, 0x90, 0xe9, 0x93, 0x38, 0xa9, 0xdc, 0x83, 0xeb,
+	0x22, 0xeb, 0x9d, 0x92, 0xfb, 0x61, 0x48, 0xc8, 0x2e, 0x34, 0x7a, 0x28, 0x24, 0x0b, 0x75, 0xc2,
+	0x29, 0x71, 0xbc, 0x7f, 0xfa, 0x91, 0xdf, 0x29, 0x0c, 0x5c, 0xd3, 0xda, 0xf9, 0x12, 0x2e, 0x9e,
+	0xaa, 0x6d, 0xf4, 0x42, 0xd6, 0x44, 0x2f, 0x74, 0x6a, 0x07, 0xe5, 0x10, 0x68, 0x96, 0x19, 0xc8,
+	0x09, 0x61, 0x5d, 0xd5, 0xf4, 0x76, 0x9f, 0xc6, 0xf2, 0x55, 0xf4, 0x37, 0x37, 0xa1, 0x9e, 0xc7,
+	0x9b, 0x59, 0xe8, 0x16, 0xac, 0x8c, 0xb3, 0x06, 0x34, 0x69, 0x70, 0xf2, 0xb9, 0xb3, 0x03, 0xc4,
+	0x4c, 0x36, 0x7d, 0x28, 0x2e, 0xc3, 0x22, 0x93, 0x38, 0xcc, 0xba, 0x8c, 0x37, 0xca, 0xfc, 0xae,
+	0xd5, 0xdd, 0x44, 0x67, 0xfb, 0xd7, 0x1a, 0xac, 0x17, 0x34, 0xab, 0xfe, 0x32, 0x0f, 0xc9, 0x23,
+	0x68, 0xde, 0x4f, 0x7f, 0xf7, 0x65, 0x3d, 0x2b, 0x79, 0xdb, 0xf4, 0x53, 0xfa, 0x05, 0xd8, 0xda,
+	0x98, 0xbd, 0x98, 0x64, 0xe4, 0x2c, 0x90, 0x9b, 0xb0, 0x92, 0x35, 0x8d, 0x93, 0x8e, 0x4a, 0xad,
+	0x64, 0xab, 0x69, 0x2e, 0xaa, 0x05, 0x67, 0x81, 0xdc, 0x4a, 0x8c, 0x55, 0x1b, 0x34, 0x6d, 0x6c,
+	0x34, 0x78, 0xad, 0x73, 0x33, 0x1a, 0x2a, 0x67, 0x81, 0x7c, 0x0b, 0x67, 0xef, 0x6b, 0xea, 0x4c,
+	0x9f, 0x54, 0xf2, 0xde, 0x64, 0x90, 0x13, 0x7a, 0xa4, 0x96, 0x53, 0x56, 0x9b, 0x7e, 0x95, 0x9d,
+	0x05, 0xf2, 0xb3, 0x05, 0xe7, 0xee, 0xa3, 0x2c, 0xbf, 0x50, 0xe4, 0xca, 0xec, 0x20, 0x27, 0xbc,
+	0x64, 0xad, 0xdd, 0xb9, 0x50, 0x35, 0xe9, 0xd3, 0x59, 0x20, 0xfb, 0x7a, 0xcf, 0x05, 0x3a, 0xc8,
+	0xc5, 0x99, 0x30, 0xc8, 0x4b, 0xd7, 0x3e, 0x69, 0x39, 0xdb, 0xe7, 0x67, 0xb7, 0xfe, 0x79, 0xde,
+	0xb6, 0xfe, 0x7d, 0xde, 0xb6, 0xfe, 0x7b, 0xde, 0xb6, 0xbe, 0xfe, 0xe8, 0xb4, 0x2f, 0x0a, 0xc6,
+	0x97, 0x0f, 0xca, 0x99, 0x17, 0x30, 0x0c, 0xe5, 0xe1, 0x92, 0xfe, 0x7e, 0xf0, 0xf1, 0xff, 0x01,
+	0x00, 0x00, 0xff, 0xff, 0xaa, 0x90, 0xca, 0xf2, 0x18, 0x11, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -2160,6 +2170,16 @@ func (m *RepoServerRevisionMetadataRequest) MarshalToSizedBuffer(dAtA []byte) (i
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	if m.CheckSignature {
+		i--
+		if m.CheckSignature {
+			dAtA[i] = 1
+		} else {
+			dAtA[i] = 0
+		}
+		i--
+		dAtA[i] = 0x18
+	}
 	if len(m.Revision) > 0 {
 		i -= len(m.Revision)
 		copy(dAtA[i:], m.Revision)
@@ -2887,6 +2907,9 @@ func (m *RepoServerRevisionMetadataRequest) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovRepository(uint64(l))
 	}
+	if m.CheckSignature {
+		n += 2
+	}
 	if m.XXX_unrecognized != nil {
 		n += len(m.XXX_unrecognized)
 	}
@@ -4844,6 +4867,26 @@ func (m *RepoServerRevisionMetadataRequest) Unmarshal(dAtA []byte) error {
 			}
 			m.Revision = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field CheckSignature", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowRepository
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				v |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.CheckSignature = bool(v != 0)
 		default:
 			iNdEx = preIndex
 			skippy, err := skipRepository(dAtA[iNdEx:])
diff --git a/reposerver/cache/cache.go b/reposerver/cache/cache.go
index a45b6a0ec..d9fc4a327 100644
--- a/reposerver/cache/cache.go
+++ b/reposerver/cache/cache.go
@@ -1,8 +1,10 @@
 package cache
 
 import (
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
+	"hash/fnv"
 	"time"
 
 	"github.com/go-redis/redis/v8"
@@ -12,6 +14,8 @@ import (
 	"github.com/argoproj/argo-cd/reposerver/apiclient"
 	cacheutil "github.com/argoproj/argo-cd/util/cache"
 	"github.com/argoproj/argo-cd/util/hash"
+
+	log "github.com/sirupsen/logrus"
 )
 
 var ErrCacheMiss = cacheutil.ErrCacheMiss
@@ -70,10 +74,48 @@ func manifestCacheKey(revision string, appSrc *appv1.ApplicationSource, namespac
 }
 
 func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, namespace string, appLabelKey string, appLabelValue string, res *CachedManifestResponse) error {
-	return c.cache.GetItem(manifestCacheKey(revision, appSrc, namespace, appLabelKey, appLabelValue), res)
+	err := c.cache.GetItem(manifestCacheKey(revision, appSrc, namespace, appLabelKey, appLabelValue), res)
+
+	if err != nil {
+		return err
+	}
+
+	hash, err := res.generateCacheEntryHash()
+	if err != nil {
+		return fmt.Errorf("Unable to generate hash value: %s", err)
+	}
+
+	// If the expected hash of the cache entry does not match the actual hash value...
+	if hash != res.CacheEntryHash {
+		log.Warnf("Manifest hash did not match expected value, treating as a cache miss: %s", appLabelValue)
+
+		err = c.DeleteManifests(revision, appSrc, namespace, appLabelKey, appLabelValue)
+		if err != nil {
+			return fmt.Errorf("Unable to delete manifest after hash mismatch, %v", err)
+		}
+
+		// Treat hash mismatches as cache misses, so that the underlying resource is reacquired
+		return ErrCacheMiss
+	}
+
+	// The expected hash matches the actual hash, so remove the hash from the returned value
+	res.CacheEntryHash = ""
+
+	return nil
 }
 
 func (c *Cache) SetManifests(revision string, appSrc *appv1.ApplicationSource, namespace string, appLabelKey string, appLabelValue string, res *CachedManifestResponse) error {
+
+	// Generate and apply the cache entry hash, before writing
+	if res != nil {
+		res = res.shallowCopy()
+		hash, err := res.generateCacheEntryHash()
+		if err != nil {
+			return fmt.Errorf("Unable to generate hash value: %s", err)
+		}
+		res.CacheEntryHash = hash
+	}
+
 	return c.cache.SetItem(manifestCacheKey(revision, appSrc, namespace, appLabelKey, appLabelValue), res, c.repoCacheExpiration, res == nil)
 }
 
@@ -106,12 +148,52 @@ func (c *Cache) SetRevisionMetadata(repoURL, revision string, item *appv1.Revisi
 	return c.cache.SetItem(revisionMetadataKey(repoURL, revision), item, c.repoCacheExpiration, false)
 }
 
+func (cmr *CachedManifestResponse) shallowCopy() *CachedManifestResponse {
+	if cmr == nil {
+		return nil
+	}
+
+	return &CachedManifestResponse{
+		CacheEntryHash:                  cmr.CacheEntryHash,
+		FirstFailureTimestamp:           cmr.FirstFailureTimestamp,
+		ManifestResponse:                cmr.ManifestResponse,
+		MostRecentError:                 cmr.MostRecentError,
+		NumberOfCachedResponsesReturned: cmr.NumberOfCachedResponsesReturned,
+		NumberOfConsecutiveFailures:     cmr.NumberOfConsecutiveFailures,
+	}
+}
+
+func (cmr *CachedManifestResponse) generateCacheEntryHash() (string, error) {
+
+	// Copy, then remove the old hash
+	copy := cmr.shallowCopy()
+	copy.CacheEntryHash = ""
+
+	// Hash the JSON representation into a base-64-encoded FNV 64a (we don't need a cryptographic hash algorithm, since this is only for detecting data corruption)
+	bytes, err := json.Marshal(copy)
+	if err != nil {
+		return "", err
+	}
+	h := fnv.New64a()
+	_, err = h.Write(bytes)
+	if err != nil {
+		return "", err
+	}
+	fnvHash := h.Sum(nil)
+	return base64.URLEncoding.EncodeToString(fnvHash), nil
+
+}
+
 // CachedManifestResponse represents a cached result of a previous manifest generation operation, including the caching
 // of a manifest generation error, plus additional information on previous failures
 type CachedManifestResponse struct {
-	ManifestResponse                *apiclient.ManifestResponse
-	MostRecentError                 string
-	FirstFailureTimestamp           int64
-	NumberOfConsecutiveFailures     int
-	NumberOfCachedResponsesReturned int
+
+	// NOTE: When adding fields to this struct, you MUST also update shallowCopy()
+
+	CacheEntryHash                  string                      `json:"cacheEntryHash"`
+	ManifestResponse                *apiclient.ManifestResponse `json:"manifestResponse"`
+	MostRecentError                 string                      `json:"mostRecentError"`
+	FirstFailureTimestamp           int64                       `json:"firstFailureTimestamp"`
+	NumberOfConsecutiveFailures     int                         `json:"numberOfConsecutiveFailures"`
+	NumberOfCachedResponsesReturned int                         `json:"numberOfCachedResponsesReturned"`
 }
diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go
index 2c51afef0..7393c7ac5 100644
--- a/reposerver/repository/repository.go
+++ b/reposerver/repository/repository.go
@@ -1,6 +1,7 @@
 package repository
 
 import (
+	"bytes"
 	"context"
 	"encoding/json"
 	"errors"
@@ -873,7 +874,20 @@ func findManifests(appPath string, repoRoot string, env *v1alpha1.Env, directory
 		} else {
 			yamlObjs, err := kube.SplitYAML(out)
 			if err != nil {
-				return status.Errorf(codes.FailedPrecondition, "Failed to unmarshal %q: %v", f.Name(), err)
+				if len(yamlObjs) > 0 {
+					// If we get here, we had a multiple objects in a single YAML file which had some
+					// valid k8s objects, but errors parsing others (within the same file). It's very
+					// likely the user messed up a portion of the YAML, so report on that.
+					return status.Errorf(codes.FailedPrecondition, "Failed to unmarshal %q: %v", f.Name(), err)
+				}
+				// Otherwise, let's see if it looks like a resource, if yes, we return error
+				if bytes.Contains(out, []byte("apiVersion:")) &&
+					bytes.Contains(out, []byte("kind:")) &&
+					bytes.Contains(out, []byte("metadata:")) {
+					return status.Errorf(codes.FailedPrecondition, "Failed to unmarshal %q: %v", f.Name(), err)
+				}
+				// Otherwise, it might be a unrelated YAML file which we will ignore
+				return nil
 			}
 			objs = append(objs, yamlObjs...)
 		}
@@ -1121,8 +1135,20 @@ func (s *Service) GetRevisionMetadata(ctx context.Context, q *apiclient.RepoServ
 	}
 	metadata, err := s.cache.GetRevisionMetadata(q.Repo.Repo, q.Revision)
 	if err == nil {
-		log.Infof("revision metadata cache hit: %s/%s", q.Repo.Repo, q.Revision)
-		return metadata, nil
+		// The logic here is that if a signature check on metadata is requested,
+		// but there is none in the cache, we handle as if we have a cache miss
+		// and re-generate the meta data. Otherwise, if there is signature info
+		// in the metadata, but none was requested, we remove it from the data
+		// that we return.
+		if q.CheckSignature && metadata.SignatureInfo == "" {
+			log.Infof("revision metadata cache hit, but need to regenerate due to missing signature info: %s/%s", q.Repo.Repo, q.Revision)
+		} else {
+			log.Infof("revision metadata cache hit: %s/%s", q.Repo.Repo, q.Revision)
+			if !q.CheckSignature {
+				metadata.SignatureInfo = ""
+			}
+			return metadata, nil
+		}
 	} else {
 		if err != reposervercache.ErrCacheMiss {
 			log.Warnf("revision metadata cache error %s/%s: %v", q.Repo.Repo, q.Revision, err)
@@ -1156,7 +1182,7 @@ func (s *Service) GetRevisionMetadata(ctx context.Context, q *apiclient.RepoServ
 
 	// Run gpg verify-commit on the revision
 	signatureInfo := ""
-	if gpg.IsGPGEnabled() {
+	if gpg.IsGPGEnabled() && q.CheckSignature {
 		cs, err := gitClient.VerifyCommitSignature(q.Revision)
 		if err != nil {
 			log.Debugf("Could not verify commit signature: %v", err)
diff --git a/reposerver/repository/repository.proto b/reposerver/repository/repository.proto
index 7802aab3d..986d14092 100644
--- a/reposerver/repository/repository.proto
+++ b/reposerver/repository/repository.proto
@@ -81,6 +81,8 @@ message RepoServerRevisionMetadataRequest {
     github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.Repository repo = 1;
     // the revision within the repo
     string revision = 2;
+    // whether to check signature on revision
+    bool checkSignature = 3;
 }
 
 // KsonnetAppSpec contains Ksonnet app response
diff --git a/server/application/application.go b/server/application/application.go
index 1f5d22f80..68bf020cc 100644
--- a/server/application/application.go
+++ b/server/application/application.go
@@ -847,6 +847,11 @@ func (s *Server) GetResource(ctx context.Context, q *application.ApplicationReso
 	if err != nil {
 		return nil, err
 	}
+
+	// make sure to use specified resource version if provided
+	if q.Version != "" {
+		res.Version = q.Version
+	}
 	obj, err := s.kubectl.GetResource(ctx, config, res.GroupKindVersion(), res.Name, res.Namespace)
 	if err != nil {
 		return nil, err
@@ -982,12 +987,22 @@ func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMe
 	if err != nil {
 		return nil, err
 	}
+	// We need to get some information with the project associated to the app,
+	// so we'll know whether GPG signatures are enforced.
+	proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr)
+	if err != nil {
+		return nil, err
+	}
 	conn, repoClient, err := s.repoClientset.NewRepoServerClient()
 	if err != nil {
 		return nil, err
 	}
 	defer io.Close(conn)
-	return repoClient.GetRevisionMetadata(ctx, &apiclient.RepoServerRevisionMetadataRequest{Repo: repo, Revision: q.GetRevision()})
+	return repoClient.GetRevisionMetadata(ctx, &apiclient.RepoServerRevisionMetadataRequest{
+		Repo:           repo,
+		Revision:       q.GetRevision(),
+		CheckSignature: len(proj.Spec.SignatureKeys) > 0,
+	})
 }
 
 func isMatchingResource(q *application.ResourcesQuery, key kube.ResourceKey) bool {
diff --git a/ui/src/app/applications/components/application-details/application-details.tsx b/ui/src/app/applications/components/application-details/application-details.tsx
index a375ba666..2444f32d9 100644
--- a/ui/src/app/applications/components/application-details/application-details.tsx
+++ b/ui/src/app/applications/components/application-details/application-details.tsx
@@ -267,7 +267,11 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{nam
                                                             const controlled = managedResources.find(item => isSameNode(selectedNode, item));
                                                             const summary = application.status.resources.find(item => isSameNode(selectedNode, item));
                                                             const controlledState = (controlled && summary && {summary, state: controlled}) || null;
-                                                            const liveState = await services.applications.getResource(application.metadata.name, selectedNode).catch(() => null);
+                                                            const resQuery = {...selectedNode};
+                                                            if (controlled && controlled.targetState) {
+                                                                resQuery.version = AppUtils.parseApiVersion(controlled.targetState.apiVersion).version;
+                                                            }
+                                                            const liveState = await services.applications.getResource(application.metadata.name, resQuery).catch(() => null);
                                                             const events =
                                                                 (liveState &&
                                                                     (await services.applications.resourceEvents(application.metadata.name, {
diff --git a/ui/src/app/applications/components/revision-form-field/revision-form-field.tsx b/ui/src/app/applications/components/revision-form-field/revision-form-field.tsx
index 2acad61af..f40386f2e 100644
--- a/ui/src/app/applications/components/revision-form-field/revision-form-field.tsx
+++ b/ui/src/app/applications/components/revision-form-field/revision-form-field.tsx
@@ -22,7 +22,7 @@ export class RevisionFormField extends React.PureComponent<RevisionFormFieldProp
                         if (src.repoURL) {
                             return services.repos
                                 .revisions(src.repoURL)
-                                .then(revisionsRes => ['HEAD'].concat(revisionsRes.branches).concat(revisionsRes.tags))
+                                .then(revisionsRes => ['HEAD'].concat(revisionsRes.branches || []).concat(revisionsRes.tags || []))
                                 .catch(() => []);
                         }
                         return [];
diff --git a/ui/src/app/applications/components/utils.tsx b/ui/src/app/applications/components/utils.tsx
index dc6c4011e..4b10840e6 100644
--- a/ui/src/app/applications/components/utils.tsx
+++ b/ui/src/app/applications/components/utils.tsx
@@ -556,3 +556,11 @@ export function handlePageVisibility<T>(src: () => Observable<T>): Observable<T>
         };
     });
 }
+
+export function parseApiVersion(apiVersion: string): {group: string; version: string} {
+    const parts = apiVersion.split('/');
+    if (parts.length > 1) {
+        return {group: parts[0], version: parts[1]};
+    }
+    return {version: parts[0], group: ''};
+}
diff --git a/ui/src/app/settings/components/project-details/project-details.tsx b/ui/src/app/settings/components/project-details/project-details.tsx
index 4dd24d49c..3237be08a 100644
--- a/ui/src/app/settings/components/project-details/project-details.tsx
+++ b/ui/src/app/settings/components/project-details/project-details.tsx
@@ -511,7 +511,7 @@ export class ProjectDetails extends React.Component<RouteComponentProps<{name: s
             proj.metadata.labels = updatedProj.metadata.labels;
             proj.spec = updatedProj.spec;
 
-            const updated = await services.projects.updateProj(proj);
+            const updated = await services.projects.update(proj);
             const globalProj = await loadGlobal(updatedProj.metadata.name);
             this.loader.setData([updated, globalProj]);
         } catch (e) {
diff --git a/ui/src/app/settings/components/project-edit-panel/project-edit-panel.scss b/ui/src/app/settings/components/project-edit-panel/project-edit-panel.scss
deleted file mode 100644
index bae8f21a5..000000000
--- a/ui/src/app/settings/components/project-edit-panel/project-edit-panel.scss
+++ /dev/null
@@ -1,30 +0,0 @@
-.project-edit-panel {
-    &__form-row {
-        position: relative;
-        input.argo-field {
-            margin-top: 29px;
-        }
-
-        .form-field__select:not(.argo-has-value) {
-            & .select__value::before {
-                content: '';
-                display: inline-block;
-            }
-        }
-
-        .fa-times {
-            position: absolute;
-            cursor: pointer;
-            left: -1.2em;
-            top: 2.2em;
-        }
-    }
-
-    form > h4 {
-        margin-top: 1em;
-    }
-
-    form > .row > .columns {
-        padding-left: 0;
-    }
-}
diff --git a/ui/src/app/settings/components/project-edit-panel/project-edit-panel.tsx b/ui/src/app/settings/components/project-edit-panel/project-edit-panel.tsx
deleted file mode 100644
index f99ad4014..000000000
--- a/ui/src/app/settings/components/project-edit-panel/project-edit-panel.tsx
+++ /dev/null
@@ -1,314 +0,0 @@
-import {AutocompleteField, FormField, FormSelect} from 'argo-ui';
-import * as React from 'react';
-import {Form, FormApi, Text} from 'react-form';
-
-import {CheckboxField, clusterTitle, DataLoader} from '../../../shared/components';
-import * as models from '../../../shared/models';
-import {ProjectParams, services} from '../../../shared/services';
-
-require('./project-edit-panel.scss');
-
-function removeEl(items: any[], index: number) {
-    items.splice(index, 1);
-    return items;
-}
-
-export const ProjectEditPanel = (props: {nameReadonly?: boolean; defaultParams?: ProjectParams; submit: (params: ProjectParams) => any; getApi?: (formApi: FormApi) => void}) => (
-    <div className='project-edit-panel'>
-        <Form
-            onSubmit={props.submit}
-            getApi={props.getApi}
-            defaultValues={{
-                sourceRepos: [],
-                destinations: [],
-                roles: [],
-                syncWindows: [],
-                clusterResourceWhitelist: [],
-                clusterResourceBlacklist: [],
-                namespaceResourceBlacklist: [],
-                namespaceResourceWhitelist: [],
-                orphanedResourceIgnoreList: [],
-                signatureKeys: [],
-                ...props.defaultParams
-            }}
-            validateError={(params: ProjectParams) => ({
-                name: !params.name && 'Project name is required'
-            })}
-            preSubmit={(params: ProjectParams) => {
-                params.clusterResourceWhitelist.forEach((obj: models.GroupKind) => {
-                    obj.group = obj.group.trim();
-                    obj.kind = obj.kind.trim();
-                });
-                params.clusterResourceBlacklist.forEach((obj: models.GroupKind) => {
-                    obj.group = obj.group.trim();
-                    obj.kind = obj.kind.trim();
-                });
-                return params;
-            }}>
-            {api => (
-                <form onSubmit={api.submitForm} role='form' className='width-control'>
-                    <h4>Summary:</h4>
-                    <div className='argo-form-row'>
-                        <FormField formApi={api} label='Project Name' componentProps={{readOnly: props.nameReadonly}} field='name' component={Text} />
-                    </div>
-                    <div className='argo-form-row'>
-                        <FormField formApi={api} label='Project Description' field='description' component={Text} />
-                    </div>
-                    <DataLoader load={() => services.repos.list().then(repos => repos.concat({repo: '*'} as models.Repository).map(repo => repo.repo))}>
-                        {repos => (
-                            <React.Fragment>
-                                <h4>Sources</h4>
-                                <div>Repositories where application manifests are permitted to be retrieved from</div>
-                                {(api.values.sourceRepos as Array<string>).map((_, i) => (
-                                    <div key={i} className='row project-edit-panel__form-row'>
-                                        <div className='columns small-12'>
-                                            <FormField
-                                                formApi={api}
-                                                field={`sourceRepos[${i}]`}
-                                                component={AutocompleteField}
-                                                componentProps={{
-                                                    items: repos
-                                                }}
-                                            />
-                                            <i className='fa fa-times' onClick={() => api.setValue('sourceRepos', removeEl(api.values.sourceRepos, i))} />
-                                        </div>
-                                    </div>
-                                ))}
-                                <a onClick={() => api.setValue('sourceRepos', api.values.sourceRepos.concat(repos[0]))}>add source</a>
-                            </React.Fragment>
-                        )}
-                    </DataLoader>
-
-                    <DataLoader load={() => services.clusters.list()}>
-                        {clusters => (
-                            <React.Fragment>
-                                <h4>Destinations</h4>
-                                <div>Cluster and namespaces where applications are permitted to be deployed to</div>
-                                <div className='argo-table-list__head'>
-                                    <div className='row'>
-                                        <div className='columns small-5'>CLUSTER</div>
-                                        <div className='columns small-5'>NAMESPACE</div>
-                                    </div>
-                                </div>
-                                {(api.values.destinations as Array<models.ApplicationDestination>).map((_, i) => (
-                                    <div key={i} className='row project-edit-panel__form-row'>
-                                        <div className='columns small-5'>
-                                            <FormSelect
-                                                field={['destinations', i, 'server']}
-                                                options={clusters
-                                                    .map(cluster => ({
-                                                        value: cluster.server,
-                                                        title: clusterTitle(cluster)
-                                                    }))
-                                                    .concat({value: '*', title: '*'})}
-                                            />
-                                        </div>
-                                        <div className='columns small-5'>
-                                            <Text className='argo-field' field={['destinations', i, 'namespace']} />
-                                        </div>
-                                        <div className='columns small-2'>
-                                            <i className='fa fa-times' onClick={() => api.setValue('destinations', removeEl(api.values.destinations, i))} />
-                                        </div>
-                                    </div>
-                                ))}
-                                <a onClick={() => api.setValue('destinations', api.values.destinations.concat({server: clusters[0], namespace: 'default'}))}>add destination</a>
-                            </React.Fragment>
-                        )}
-                    </DataLoader>
-
-                    <React.Fragment>
-                        <h4>Whitelisted Cluster Resources</h4>
-                        <div>Cluster-scoped K8s API Groups and Kinds which are permitted to be deployed</div>
-                        <div className='argo-table-list__head'>
-                            <div className='row'>
-                                <div className='columns small-5'>GROUP</div>
-                                <div className='columns small-5'>KIND</div>
-                            </div>
-                        </div>
-                        {(api.values.clusterResourceWhitelist as Array<models.GroupKind>).map((_, i) => (
-                            <div key={i} className='argo-table-list__row'>
-                                <div className='row'>
-                                    <div className='columns small-5'>
-                                        <Text className='argo-field' field={['clusterResourceWhitelist', i, 'group']} />
-                                    </div>
-                                    <div className='columns small-5'>
-                                        <Text className='argo-field' field={['clusterResourceWhitelist', i, 'kind']} />
-                                    </div>
-                                    <div className='columns small-2'>
-                                        <i className='fa fa-times' onClick={() => api.setValue('clusterResourceWhitelist', removeEl(api.values.clusterResourceWhitelist, i))} />
-                                    </div>
-                                </div>
-                            </div>
-                        ))}
-                        <a onClick={() => api.setValue('clusterResourceWhitelist', api.values.clusterResourceWhitelist.concat({group: '', kind: ''}))}>
-                            whitelist new cluster resource
-                        </a>
-                    </React.Fragment>
-
-                    <React.Fragment>
-                        <h4>Blacklisted Cluster Resources</h4>
-                        <div>Cluster-scoped K8s API Groups and Kinds which are not permitted to be deployed</div>
-                        <div className='argo-table-list__head'>
-                            <div className='row'>
-                                <div className='columns small-5'>GROUP</div>
-                                <div className='columns small-5'>KIND</div>
-                            </div>
-                        </div>
-                        {(api.values.clusterResourceBlacklist as Array<models.GroupKind>).map((_, i) => (
-                            <div key={i} className='argo-table-list__row'>
-                                <div className='row'>
-                                    <div className='columns small-5'>
-                                        <Text className='argo-field' field={['clusterResourceBlacklist', i, 'group']} />
-                                    </div>
-                                    <div className='columns small-5'>
-                                        <Text className='argo-field' field={['clusterResourceBlacklist', i, 'kind']} />
-                                    </div>
-                                    <div className='columns small-2'>
-                                        <i className='fa fa-times' onClick={() => api.setValue('clusterResourceBlacklist', removeEl(api.values.clusterResourceBlacklist, i))} />
-                                    </div>
-                                </div>
-                            </div>
-                        ))}
-                        <a onClick={() => api.setValue('clusterResourceBlacklist', api.values.clusterResourceBlacklist.concat({group: '', kind: ''}))}>
-                            blacklist new cluster resource
-                        </a>
-                    </React.Fragment>
-
-                    <React.Fragment>
-                        <h4>Blacklisted Namespaced Resources</h4>
-                        <div>
-                            Namespace-scoped K8s API Groups and Kinds which are <strong>prohibited</strong> from being deployed
-                        </div>
-                        <div className='argo-table-list__head'>
-                            <div className='row'>
-                                <div className='columns small-5'>GROUP</div>
-                                <div className='columns small-5'>KIND</div>
-                            </div>
-                        </div>
-                        {(api.values.namespaceResourceBlacklist as Array<models.GroupKind>).map((_, i) => (
-                            <div key={i} className='argo-table-list__row'>
-                                <div className='row'>
-                                    <div className='columns small-5'>
-                                        <Text className='argo-field' field={['namespaceResourceBlacklist', i, 'group']} />
-                                    </div>
-                                    <div className='columns small-5'>
-                                        <Text className='argo-field' field={['namespaceResourceBlacklist', i, 'kind']} />
-                                    </div>
-                                    <div className='columns small-2'>
-                                        <i className='fa fa-times' onClick={() => api.setValue('namespaceResourceBlacklist', removeEl(api.values.namespaceResourceBlacklist, i))} />
-                                    </div>
-                                </div>
-                            </div>
-                        ))}
-                        <a onClick={() => api.setValue('namespaceResourceBlacklist', api.values.namespaceResourceBlacklist.concat({group: '', kind: ''}))}>
-                            blacklist new namespaced resource
-                        </a>
-                    </React.Fragment>
-
-                    <React.Fragment>
-                        <h4>Whitelisted Namespaced Resources</h4>
-                        <div>
-                            Namespace-scoped K8s API Groups and Kinds which are <strong>permitted</strong> to deploy
-                        </div>
-                        <div className='argo-table-list__head'>
-                            <div className='row'>
-                                <div className='columns small-5'>GROUP</div>
-                                <div className='columns small-5'>KIND</div>
-                            </div>
-                        </div>
-                        {(api.values.namespaceResourceWhitelist as Array<models.GroupKind>).map((_, i) => (
-                            <div key={i} className='argo-table-list__row'>
-                                <div className='row'>
-                                    <div className='columns small-5'>
-                                        <Text className='argo-field' field={['namespaceResourceWhitelist', i, 'group']} />
-                                    </div>
-                                    <div className='columns small-5'>
-                                        <Text className='argo-field' field={['namespaceResourceWhitelist', i, 'kind']} />
-                                    </div>
-                                    <div className='columns small-2'>
-                                        <i className='fa fa-times' onClick={() => api.setValue('namespaceResourceWhitelist', removeEl(api.values.namespaceResourceWhitelist, i))} />
-                                    </div>
-                                </div>
-                            </div>
-                        ))}
-                        <a onClick={() => api.setValue('namespaceResourceWhitelist', api.values.namespaceResourceWhitelist.concat({group: '', kind: ''}))}>
-                            whitelist new namespaced resource
-                        </a>
-                    </React.Fragment>
-
-                    <DataLoader load={() => services.gpgkeys.list().then(gpgkeys => gpgkeys.map(gpgkey => gpgkey.keyID))}>
-                        {gpgkeys => (
-                            <React.Fragment>
-                                <h4>Required signature keys</h4>
-                                <div>GnuPG key IDs which commits to be synced to must be signed with</div>
-                                {(api.values.signatureKeys as Array<string>).map((_, i) => (
-                                    <div key={i} className='row project-edit-panel__form-row'>
-                                        <div className='columns small-12'>
-                                            <FormField
-                                                formApi={api}
-                                                field={`signatureKeys[${i}].keyID`}
-                                                component={AutocompleteField}
-                                                componentProps={{
-                                                    items: gpgkeys
-                                                }}
-                                            />
-                                            <i className='fa fa-times' onClick={() => api.setValue('signatureKeys', removeEl(api.values.signatureKeys, i))} />
-                                        </div>
-                                    </div>
-                                ))}
-                                <a onClick={() => api.setValue('signatureKeys', api.values.signatureKeys.concat(gpgkeys[0]))}>add GnuPG key ID</a>
-                            </React.Fragment>
-                        )}
-                    </DataLoader>
-
-                    <React.Fragment>
-                        <h4>Orphaned Resource Monitoring</h4>
-                        <div>Enables monitoring of top level resources in the application target namespace</div>
-                        <FormField formApi={api} label='Enabled' field='orphanedResourcesEnabled' component={CheckboxField} />
-                        {api.values.orphanedResourcesEnabled && <FormField formApi={api} label='Warn' field='orphanedResourcesWarn' component={CheckboxField} />}
-                    </React.Fragment>
-
-                    {api.values.orphanedResourcesEnabled && (
-                        <React.Fragment>
-                            <h4>Orphaned Resources Ignore List</h4>
-                            <div>
-                                Define resources that ArgoCD should <strong>not</strong> report them as orphaned
-                            </div>
-                            <div className='argo-table-list__head'>
-                                <div className='row'>
-                                    <div className='columns small-3'>GROUP</div>
-                                    <div className='columns small-3'>KIND</div>
-                                    <div className='columns small-4'>NAME</div>
-                                </div>
-                            </div>
-                            {(api.values.orphanedResourceIgnoreList as Array<models.OrphanedResource>).map((_, i) => (
-                                <div key={i} className='argo-table-list__row'>
-                                    <div className='row'>
-                                        <div className='columns small-3'>
-                                            <Text className='argo-field' field={['orphanedResourceIgnoreList', i, 'group']} />
-                                        </div>
-                                        <div className='columns small-3'>
-                                            <Text className='argo-field' field={['orphanedResourceIgnoreList', i, 'kind']} />
-                                        </div>
-                                        <div className='columns small-4'>
-                                            <Text className='argo-field' field={['orphanedResourceIgnoreList', i, 'name']} />
-                                        </div>
-                                        <div className='columns small-2'>
-                                            <i
-                                                className='fa fa-times'
-                                                onClick={() => api.setValue('orphanedResourceIgnoreList', removeEl(api.values.orphanedResourceIgnoreList, i))}
-                                            />
-                                        </div>
-                                    </div>
-                                </div>
-                            ))}
-                            <a onClick={() => api.setValue('orphanedResourceIgnoreList', api.values.orphanedResourceIgnoreList.concat({group: '', kind: '', name: ''}))}>
-                                add new resource to orphaned ignore list
-                            </a>
-                        </React.Fragment>
-                    )}
-                </form>
-            )}
-        </Form>
-    </div>
-);
diff --git a/ui/src/app/settings/components/projects-list/projects-list.tsx b/ui/src/app/settings/components/projects-list/projects-list.tsx
index 3269931df..4be22ca9b 100644
--- a/ui/src/app/settings/components/projects-list/projects-list.tsx
+++ b/ui/src/app/settings/components/projects-list/projects-list.tsx
@@ -1,15 +1,14 @@
-import {NotificationType, SlidingPanel} from 'argo-ui';
+import {FormField, NotificationType, SlidingPanel} from 'argo-ui';
 import * as React from 'react';
-import {FormApi} from 'react-form';
+import {Form, FormApi, Text} from 'react-form';
 
 import {DataLoader, EmptyState, ErrorNotification, Page, Query} from '../../../shared/components';
 import {Consumer} from '../../../shared/context';
+import {Project} from '../../../shared/models';
 import {services} from '../../../shared/services';
-import {ProjectEditPanel} from '../project-edit-panel/project-edit-panel';
 
 export class ProjectsList extends React.Component {
     private formApi: FormApi;
-    private loader: DataLoader;
 
     public render() {
         return (
@@ -22,7 +21,7 @@ export class ProjectsList extends React.Component {
                             actionMenu: {className: 'fa fa-plus', items: [{title: 'New Project', action: () => ctx.navigation.goto('.', {add: true})}]}
                         }}>
                         <div className='projects argo-container'>
-                            <DataLoader load={() => services.projects.list()} ref={loader => (this.loader = loader)}>
+                            <DataLoader load={() => services.projects.list()}>
                                 {projects =>
                                     (projects.length > 0 && (
                                         <div className='argo-table-list argo-table-list--clickable'>
@@ -60,6 +59,7 @@ export class ProjectsList extends React.Component {
                                 <SlidingPanel
                                     isShown={params.get('add') === 'true'}
                                     onClose={() => ctx.navigation.goto('.', {add: null})}
+                                    isMiddle={true}
                                     header={
                                         <div>
                                             <button onClick={() => ctx.navigation.goto('.', {add: null})} className='argo-button argo-button--base-o'>
@@ -70,21 +70,37 @@ export class ProjectsList extends React.Component {
                                             </button>
                                         </div>
                                     }>
-                                    <ProjectEditPanel
+                                    <Form
+                                        defaultValues={{metadata: {}, spec: {}}}
                                         getApi={api => (this.formApi = api)}
-                                        submit={async projParams => {
+                                        validateError={(p: Project) => ({
+                                            'metadata.name': !p.metadata.name && 'Project name is required'
+                                        })}
+                                        onSubmit={async (proj: Project) => {
                                             try {
-                                                await services.projects.create(projParams);
-                                                ctx.navigation.goto('.', {add: null});
-                                                this.loader.reload();
+                                                await services.projects.create(proj);
+                                                ctx.navigation.goto(`./${proj.metadata.name}`, {add: null});
                                             } catch (e) {
                                                 ctx.notifications.show({
                                                     content: <ErrorNotification title='Unable to create project' e={e} />,
                                                     type: NotificationType.Error
                                                 });
                                             }
-                                        }}
-                                    />
+                                        }}>
+                                        {api => (
+                                            <form onSubmit={api.submitForm} role='form' className='width-control'>
+                                                <div className='white-box'>
+                                                    <p>GENERAL</p>
+                                                    <div className='argo-form-row'>
+                                                        <FormField formApi={api} label='Name' field='metadata.name' component={Text} />
+                                                    </div>
+                                                    <div className='argo-form-row'>
+                                                        <FormField formApi={api} label='Description' field='spec.description' component={Text} />
+                                                    </div>
+                                                </div>
+                                            </form>
+                                        )}
+                                    </Form>
                                 </SlidingPanel>
                             )}
                         </Query>
diff --git a/ui/src/app/shared/services/index.ts b/ui/src/app/shared/services/index.ts
index 65b91a2e4..8a8d9b011 100644
--- a/ui/src/app/shared/services/index.ts
+++ b/ui/src/app/shared/services/index.ts
@@ -41,5 +41,5 @@ export const services: Services = {
     gpgkeys: new GnuPGPublicKeyService()
 };
 
-export {ProjectParams, ProjectRoleParams, CreateJWTTokenParams, DeleteJWTTokenParams, JWTTokenResponse} from './projects-service';
+export {ProjectRoleParams, CreateJWTTokenParams, DeleteJWTTokenParams, JWTTokenResponse} from './projects-service';
 export * from './view-preferences-service';
diff --git a/ui/src/app/shared/services/projects-service.ts b/ui/src/app/shared/services/projects-service.ts
index 462f14ae5..a3324aac6 100644
--- a/ui/src/app/shared/services/projects-service.ts
+++ b/ui/src/app/shared/services/projects-service.ts
@@ -2,24 +2,6 @@ import requests from './requests';
 
 import * as models from '../models';
 
-export interface ProjectParams {
-    name: string;
-    description: string;
-    labels: {[name: string]: string};
-    sourceRepos: string[];
-    destinations: models.ApplicationDestination[];
-    roles: models.ProjectRole[];
-    clusterResourceWhitelist: models.GroupKind[];
-    clusterResourceBlacklist: models.GroupKind[];
-    namespaceResourceBlacklist: models.GroupKind[];
-    namespaceResourceWhitelist: models.GroupKind[];
-    orphanedResourceIgnoreList: models.OrphanedResource[];
-    signatureKeys: models.ProjectSignatureKey[];
-    orphanedResourcesEnabled: boolean;
-    orphanedResourcesWarn: boolean;
-    syncWindows: models.SyncWindow[];
-}
-
 export interface CreateJWTTokenParams {
     project: string;
     role: string;
@@ -71,25 +53,6 @@ function paramsToProjRole(params: ProjectRoleParams): models.ProjectRole {
     };
 }
 
-function paramsToProj(params: ProjectParams) {
-    return {
-        metadata: {name: params.name, labels: params.labels},
-        spec: {
-            description: params.description,
-            sourceRepos: params.sourceRepos,
-            destinations: params.destinations,
-            roles: params.roles,
-            syncWindows: params.syncWindows,
-            clusterResourceWhitelist: params.clusterResourceWhitelist,
-            clusterResourceBlacklist: params.clusterResourceBlacklist,
-            namespaceResourceBlacklist: params.namespaceResourceBlacklist,
-            namespaceResourceWhitelist: params.namespaceResourceWhitelist,
-            signatureKeys: params.signatureKeys,
-            orphanedResources: (params.orphanedResourcesEnabled && {warn: !!params.orphanedResourcesWarn, ignore: params.orphanedResourceIgnoreList}) || null
-        }
-    };
-}
-
 export class ProjectsService {
     public list(...fields: string[]): Promise<models.Project[]> {
         return requests
@@ -111,29 +74,20 @@ export class ProjectsService {
         return requests.delete(`/projects/${name}`).then(() => true);
     }
 
-    public create(params: ProjectParams): Promise<models.Project> {
+    public create(project: models.Project): Promise<models.Project> {
         return requests
             .post('/projects')
-            .send({project: paramsToProj(params)})
+            .send({project})
             .then(res => res.body as models.Project);
     }
 
-    public async updateProj(project: models.Project): Promise<models.Project> {
+    public async update(project: models.Project): Promise<models.Project> {
         return requests
             .put(`/projects/${project.metadata.name}`)
             .send({project})
             .then(res => res.body as models.Project);
     }
 
-    public async update(params: ProjectParams): Promise<models.Project> {
-        const proj = await this.get(params.name);
-        const update = paramsToProj(params);
-        return requests
-            .put(`/projects/${params.name}`)
-            .send({project: {...proj, spec: update.spec}})
-            .then(res => res.body as models.Project);
-    }
-
     public getSyncWindows(name: string): Promise<models.SyncWindowsState> {
         return requests
             .get(`/projects/${name}/syncwindows`)
diff --git a/util/cache/inmemory.go b/util/cache/inmemory.go
index 1cfe88e46..7064b7108 100644
--- a/util/cache/inmemory.go
+++ b/util/cache/inmemory.go
@@ -54,3 +54,25 @@ func (i *InMemoryCache) OnUpdated(ctx context.Context, key string, callback func
 func (i *InMemoryCache) NotifyUpdated(key string) error {
 	return nil
 }
+
+// Items return a list of items in the cache; requires passing a constructor function
+// so that the items can be decoded from gob format.
+func (i *InMemoryCache) Items(createNewObject func() interface{}) (map[string]interface{}, error) {
+
+	result := map[string]interface{}{}
+
+	for key, value := range i.memCache.Items() {
+
+		buf := value.Object.(bytes.Buffer)
+		obj := createNewObject()
+		err := gob.NewDecoder(&buf).Decode(obj)
+		if err != nil {
+			return nil, err
+		}
+
+		result[key] = obj
+
+	}
+
+	return result, nil
+}
diff --git a/util/log/logrus.go b/util/log/logrus.go
index ecb725a61..966fa5fbe 100644
--- a/util/log/logrus.go
+++ b/util/log/logrus.go
@@ -1,11 +1,15 @@
 package log
 
 import (
+	"fmt"
+
 	adapter "github.com/bombsimon/logrusr"
 	"github.com/go-logr/logr"
 	"github.com/sirupsen/logrus"
 )
 
 func NewLogrusLogger(fieldLogger logrus.FieldLogger) logr.Logger {
-	return adapter.NewLogger(fieldLogger)
+	return adapter.NewLoggerWithFormatter(fieldLogger, func(val interface{}) string {
+		return fmt.Sprintf("%v", val)
+	})
 }


Test patch:
diff --git a/reposerver/cache/cache_test.go b/reposerver/cache/cache_test.go
index 6dae34ceb..bc60fb2c5 100644
--- a/reposerver/cache/cache_test.go
+++ b/reposerver/cache/cache_test.go
@@ -1,6 +1,9 @@
 package cache
 
 import (
+	"encoding/json"
+	"errors"
+	"strings"
 	"testing"
 	"time"
 
@@ -8,6 +11,7 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
+	appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
 	"github.com/argoproj/argo-cd/reposerver/apiclient"
 	cacheutil "github.com/argoproj/argo-cd/util/cache"
 )
@@ -120,3 +124,174 @@ func TestAddCacheFlagsToCmd(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, 24*time.Hour, cache.repoCacheExpiration)
 }
+
+func TestCachedManifestResponse_HashBehavior(t *testing.T) {
+
+	inMemCache := cacheutil.NewInMemoryCache(1 * time.Hour)
+
+	repoCache := NewCache(
+		cacheutil.NewCache(inMemCache),
+		1*time.Minute,
+	)
+
+	response := apiclient.ManifestResponse{
+		Namespace: "default",
+		Revision:  "revision",
+		Manifests: []string{"sample-text"},
+	}
+	appSrc := &appv1.ApplicationSource{}
+	appKey := "key"
+	appValue := "value"
+
+	// Set the value in the cache
+	store := &CachedManifestResponse{
+		FirstFailureTimestamp:           0,
+		ManifestResponse:                &response,
+		MostRecentError:                 "",
+		NumberOfCachedResponsesReturned: 0,
+		NumberOfConsecutiveFailures:     0,
+	}
+	err := repoCache.SetManifests(response.Revision, appSrc, response.Namespace, appKey, appValue, store)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Get the cache entry of the set value directly from the in memory cache, and check the values
+	var cacheKey string
+	var cmr *CachedManifestResponse
+	{
+
+		items := getInMemoryCacheContents(t, inMemCache)
+
+		assert.Equal(t, len(items), 1)
+
+		for key, val := range items {
+			cmr = val
+			cacheKey = key
+		}
+		assert.NotEmpty(t, cmr.CacheEntryHash)
+		assert.NotNil(t, cmr.ManifestResponse)
+		assert.Equal(t, cmr.ManifestResponse, store.ManifestResponse)
+
+		regeneratedHash, err := cmr.generateCacheEntryHash()
+		if err != nil {
+			t.Fatal(err)
+		}
+		assert.Equal(t, cmr.CacheEntryHash, regeneratedHash)
+	}
+
+	// Retrieve the value using 'GetManifests' and confirm it works
+	retrievedVal := &CachedManifestResponse{}
+	err = repoCache.GetManifests(response.Revision, appSrc, response.Namespace, appKey, appValue, retrievedVal)
+	if err != nil {
+		t.Fatal(err)
+	}
+	assert.Equal(t, retrievedVal, store)
+
+	// Corrupt the hash so that it doesn't match
+	{
+		newCmr := cmr.shallowCopy()
+		newCmr.CacheEntryHash = "!bad-hash!"
+
+		err := inMemCache.Set(&cacheutil.Item{
+			Key:    cacheKey,
+			Object: &newCmr,
+		})
+		if err != nil {
+			t.Fatal(err)
+		}
+
+	}
+
+	// Retrieve the value using GetManifests and confirm it returns a cache miss
+	retrievedVal = &CachedManifestResponse{}
+	err = repoCache.GetManifests(response.Revision, appSrc, response.Namespace, appKey, appValue, retrievedVal)
+
+	assert.True(t, err == cacheutil.ErrCacheMiss)
+
+	// Verify that the hash mismatch item has been deleted
+	items := getInMemoryCacheContents(t, inMemCache)
+	assert.Equal(t, len(items), 0)
+
+}
+
+func getInMemoryCacheContents(t *testing.T, inMemCache *cacheutil.InMemoryCache) map[string]*CachedManifestResponse {
+	items, err := inMemCache.Items(func() interface{} { return &CachedManifestResponse{} })
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	result := map[string]*CachedManifestResponse{}
+	for key, val := range items {
+		obj, ok := val.(*CachedManifestResponse)
+		if !ok {
+			t.Fatal(errors.New("Unexpected type in cache"))
+		}
+
+		result[key] = obj
+	}
+
+	return result
+}
+
+func TestCachedManifestResponse_ShallowCopy(t *testing.T) {
+
+	pre := &CachedManifestResponse{
+		CacheEntryHash:        "value",
+		FirstFailureTimestamp: 1,
+		ManifestResponse: &apiclient.ManifestResponse{
+			Manifests: []string{"one", "two"},
+		},
+		MostRecentError:                 "error",
+		NumberOfCachedResponsesReturned: 2,
+		NumberOfConsecutiveFailures:     3,
+	}
+
+	post := pre.shallowCopy()
+	assert.Equal(t, pre, post)
+
+	unequal := &CachedManifestResponse{
+		CacheEntryHash:        "diff-value",
+		FirstFailureTimestamp: 1,
+		ManifestResponse: &apiclient.ManifestResponse{
+			Manifests: []string{"one", "two"},
+		},
+		MostRecentError:                 "error",
+		NumberOfCachedResponsesReturned: 2,
+		NumberOfConsecutiveFailures:     3,
+	}
+	assert.NotEqual(t, pre, unequal)
+}
+
+func TestCachedManifestResponse_ShallowCopyExpectedFields(t *testing.T) {
+
+	// Attempt to ensure that the developer updated CachedManifestResponse.shallowCopy(), by doing a sanity test of the structure here
+
+	val := &CachedManifestResponse{}
+
+	str, err := json.Marshal(val)
+	if err != nil {
+		assert.FailNow(t, "Unable to marshal", err)
+		return
+	}
+
+	jsonMap := map[string]interface{}{}
+	err = json.Unmarshal(str, &jsonMap)
+	if err != nil {
+		assert.FailNow(t, "Unable to unmarshal", err)
+		return
+	}
+
+	expectedFields := []string{"cacheEntryHash", "manifestResponse", "mostRecentError", "firstFailureTimestamp",
+		"numberOfConsecutiveFailures", "numberOfCachedResponsesReturned"}
+
+	assert.Equal(t, len(jsonMap), len(expectedFields))
+
+	// If this test failed, you probably also forgot to update CachedManifestResponse.shallowCopy(), so
+	// go do that first :)
+
+	for _, expectedField := range expectedFields {
+		assert.Truef(t, strings.Contains(string(str), "\""+expectedField+"\""), "Missing field: %s", expectedField)
+	}
+
+}
diff --git a/reposerver/repository/repository_test.go b/reposerver/repository/repository_test.go
index a2ac2ab0c..f243cdc49 100644
--- a/reposerver/repository/repository_test.go
+++ b/reposerver/repository/repository_test.go
@@ -188,6 +188,17 @@ func TestRecurseManifestsInDir(t *testing.T) {
 	assert.Equal(t, 2, len(res1.Manifests))
 }
 
+func TestInvalidManifestsInDir(t *testing.T) {
+	service := newService(".")
+
+	src := argoappv1.ApplicationSource{Path: "./testdata/invalid-manifests", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}}
+
+	q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src}
+
+	_, err := service.GenerateManifest(context.Background(), &q)
+	assert.NotNil(t, err)
+}
+
 func TestGenerateJsonnetManifestInDir(t *testing.T) {
 	service := newService(".")
 
@@ -963,8 +974,9 @@ func TestGetRevisionMetadata(t *testing.T) {
 	}, nil)
 
 	res, err := service.GetRevisionMetadata(context.Background(), &apiclient.RepoServerRevisionMetadataRequest{
-		Repo:     &argoappv1.Repository{},
-		Revision: "c0b400fc458875d925171398f9ba9eabd5529923",
+		Repo:           &argoappv1.Repository{},
+		Revision:       "c0b400fc458875d925171398f9ba9eabd5529923",
+		CheckSignature: true,
 	})
 
 	assert.NoError(t, err)
@@ -972,7 +984,34 @@ func TestGetRevisionMetadata(t *testing.T) {
 	assert.Equal(t, now, res.Date.Time)
 	assert.Equal(t, "author", res.Author)
 	assert.EqualValues(t, []string{"tag1", "tag2"}, res.Tags)
+	assert.NotEmpty(t, res.SignatureInfo)
 
+	// Cache hit - signature info should not be in result
+	res, err = service.GetRevisionMetadata(context.Background(), &apiclient.RepoServerRevisionMetadataRequest{
+		Repo:           &argoappv1.Repository{},
+		Revision:       "c0b400fc458875d925171398f9ba9eabd5529923",
+		CheckSignature: false,
+	})
+	assert.NoError(t, err)
+	assert.Empty(t, res.SignatureInfo)
+
+	// Enforce cache miss - signature info should not be in result
+	res, err = service.GetRevisionMetadata(context.Background(), &apiclient.RepoServerRevisionMetadataRequest{
+		Repo:           &argoappv1.Repository{},
+		Revision:       "c0b400fc458875d925171398f9ba9eabd5529924",
+		CheckSignature: false,
+	})
+	assert.NoError(t, err)
+	assert.Empty(t, res.SignatureInfo)
+
+	// Cache hit on previous entry that did not have signature info
+	res, err = service.GetRevisionMetadata(context.Background(), &apiclient.RepoServerRevisionMetadataRequest{
+		Repo:           &argoappv1.Repository{},
+		Revision:       "c0b400fc458875d925171398f9ba9eabd5529924",
+		CheckSignature: true,
+	})
+	assert.NoError(t, err)
+	assert.NotEmpty(t, res.SignatureInfo)
 }
 
 func TestGetSignatureVerificationResult(t *testing.T) {
diff --git a/reposerver/repository/testdata/invalid-manifests/bad.yaml b/reposerver/repository/testdata/invalid-manifests/bad.yaml
new file mode 100644
index 000000000..3a01dbaf2
--- /dev/null
+++ b/reposerver/repository/testdata/invalid-manifests/bad.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ template "stuff.fullname" . }}-init
+  labels:
+    app: {{ template "stuff.name" . }}
+    chart: {{ template "stuff.chart" . }}
+    release: {{ .Release.Name }}
+    heritage: {{ .Release.Service }}
+data:
+  {{- $files := .Files }}
+    {{-  range tuple "init/init.sh" }}
+    {{ . }}: |-
+    {{ $files.Get . }}
+    {{ end }}
\ No newline at end of file
diff --git a/test/e2e/sync_waves_test.go b/test/e2e/sync_waves_test.go
index 49fc43453..5308b0ac5 100644
--- a/test/e2e/sync_waves_test.go
+++ b/test/e2e/sync_waves_test.go
@@ -36,17 +36,6 @@ func TestFixingDegradedApp(t *testing.T) {
 		When().
 		Sync().
 		Then().
-		Expect(OperationPhaseIs(OperationFailed)).
-		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
-		Expect(HealthIs(health.HealthStatusMissing)).
-		Expect(ResourceResultNumbering(1)).
-		Expect(ResourceSyncStatusIs("Pod", "pod-1", SyncStatusCodeSynced)).
-		Expect(ResourceHealthIs("Pod", "pod-1", health.HealthStatusHealthy)).
-		Expect(ResourceSyncStatusIs("Pod", "pod-2", SyncStatusCodeOutOfSync)).
-		Expect(ResourceHealthIs("Pod", "pod-2", health.HealthStatusMissing)).
-		When().
-		Sync().
-		Then().
 		Expect(OperationPhaseIs(OperationSucceeded)).
 		Expect(SyncStatusIs(SyncStatusCodeSynced)).
 		Expect(HealthIs(health.HealthStatusHealthy)).
diff --git a/test/e2e/testdata/app-exclusions/deploymentDoc.yaml b/test/e2e/testdata/app-exclusions/deploymentDoc.yaml
new file mode 100644
index 000000000..e8d058fb6
--- /dev/null
+++ b/test/e2e/testdata/app-exclusions/deploymentDoc.yaml
@@ -0,0 +1,16 @@
+##Purpose of this file is to test this kind of files should be ignored from marshalling since it is lacking of apiVersion, kind, and metadata
+spec:
+  replicas: 0
+  selector:
+    matchLabels:
+      app: nginx
+  template:
+    metadata:
+      labels:
+        app: nginx
+    spec:
+      containers:
+        - name: nginx
+          image: nginx:1.17.4-alpine
+          ports:
+            - containerPort: 80
diff --git a/util/helm/testdata/helm2-dependency/requirements.lock b/util/helm/testdata/helm2-dependency/requirements.lock
index cb3439862..35fb82343 100644
--- a/util/helm/testdata/helm2-dependency/requirements.lock
+++ b/util/helm/testdata/helm2-dependency/requirements.lock
@@ -1,6 +1,6 @@
 dependencies:
 - name: mariadb
-  repository: https://kubernetes-charts.storage.googleapis.com/
-  version: 4.3.1
-digest: sha256:82a0e5374376169d2ecf7d452c18a2ed93507f5d17c3393a1457f9ffad7e9b26
-generated: 2018-08-02T22:07:51.905271776Z
+  repository: https://charts.helm.sh/stable
+  version: 4.4.2
+digest: sha256:c07f89818ebcd92a4dbaff719ebedc14e2569ee27447d3abf739e2dd11d13fcb
+generated: "2020-11-03T09:04:00.781031-08:00"
diff --git a/util/helm/testdata/helm2-dependency/requirements.yaml b/util/helm/testdata/helm2-dependency/requirements.yaml
index a894b8b3b..85c98944f 100644
--- a/util/helm/testdata/helm2-dependency/requirements.yaml
+++ b/util/helm/testdata/helm2-dependency/requirements.yaml
@@ -1,7 +1,7 @@
 dependencies:
 - name: mariadb
   version: 4.x.x
-  repository: https://kubernetes-charts.storage.googleapis.com/
+  repository: https://charts.helm.sh/stable
   condition: mariadb.enabled
   tags:
     - wordpress-database