Repository: operator-framework/operator-sdk
Pull request #4939 (2021-05-23 18:54:00)

add: new community validator to check bundles
**Description**

Add a new community-validator which implemented in the API (https://github.com/operator-framework/api/pull/120)

**Checklist**

If the pull request includes user-facing changes, extra documentation is required:
- [x] Add a new changelog fragment in `changelog/fragments` (see [`changelog/fragments/00-template.yaml`](https://github.com/operator-framework/operator-sdk/tree/master/changelog/fragments/00-template.yaml))
- [x] Add or update relevant sections of the docs website in [`website/content/en/docs`](https://github.com/operator-framework/operator-sdk/tree/master/website/content/en/docs)

**Note**
- https://github.com/operator-framework/olm-docs/pull/155
- 


Patch:
diff --git a/changelog/fragments/community-validator.yaml b/changelog/fragments/community-validator.yaml
new file mode 100644
index 00000000..5b7e77f0
--- /dev/null
+++ b/changelog/fragments/community-validator.yaml
@@ -0,0 +1,16 @@
+# entries is a list of entries to include in
+# release notes and/or the migration guide
+entries:
+  - description: >
+      Added new bundle validator to check the bundle against the Community Operator criteria in the alpha stage. For futher information run `operator-sdk bundle validate --list-optional` and `operator-sdk bundle validate --help`.
+
+    # kind is one of:
+    # - addition
+    # - change
+    # - deprecation
+    # - removal
+    # - bugfix
+    kind: "addition"
+
+    # Is this a breaking change?
+    breaking: false
diff --git a/changelog/fragments/helm-randalphanum.yaml b/changelog/fragments/helm-randalphanum.yaml
new file mode 100644
index 00000000..ade97dfc
--- /dev/null
+++ b/changelog/fragments/helm-randalphanum.yaml
@@ -0,0 +1,4 @@
+entries:
+  - description: >
+      For Helm-based operators, fixed a bug where deployed and candidate release comparison was always false when an RNG was used to derive some manifest value, resulting in the chart release constantly upgrading
+    kind: bugfix
diff --git a/go.mod b/go.mod
index 9c20252e..bb716d79 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
 	github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2
 	github.com/onsi/ginkgo v1.15.2
 	github.com/onsi/gomega v1.11.0
-	github.com/operator-framework/api v0.8.1
+	github.com/operator-framework/api v0.8.2-0.20210526151024-41d37db9141f
 	github.com/operator-framework/java-operator-plugins v0.0.0-20210525141944-8303c38a876d
 	github.com/operator-framework/operator-lib v0.4.1
 	github.com/operator-framework/operator-registry v1.15.3
diff --git a/go.sum b/go.sum
index 5555385b..b95a9a79 100644
--- a/go.sum
+++ b/go.sum
@@ -768,8 +768,8 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh
 github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
 github.com/operator-framework/api v0.3.22/go.mod h1:GVNiB6AQucwdZz3ZFXNv9HtcLOzcFnr6O/QldzKG93g=
 github.com/operator-framework/api v0.5.2/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY=
-github.com/operator-framework/api v0.8.1 h1:eFWAV70bvnQfNFM1dmBOY4y5u/WGZHV/SVDDl5WNsMk=
-github.com/operator-framework/api v0.8.1/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY=
+github.com/operator-framework/api v0.8.2-0.20210526151024-41d37db9141f h1:VX19pxX9NNJNoh0kfS0PSzK4lOInXh4+4VNlbaED/gg=
+github.com/operator-framework/api v0.8.2-0.20210526151024-41d37db9141f/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY=
 github.com/operator-framework/java-operator-plugins v0.0.0-20210525141944-8303c38a876d h1:HE4CQY27vaQWQnk7oD2mku417tKyOz6UQHOUNVCuFHU=
 github.com/operator-framework/java-operator-plugins v0.0.0-20210525141944-8303c38a876d/go.mod h1:QFcG224Nx8VMwBEqxpZbT17LkISNzlOjY1/r4OQBYic=
 github.com/operator-framework/operator-lib v0.4.1 h1:Eh4JHs+LAWeC85ZMHXJ9RXg7G5grYx8J29TkOw8003s=
diff --git a/internal/cmd/operator-sdk/bundle/validate/cmd.go b/internal/cmd/operator-sdk/bundle/validate/cmd.go
index c604ace1..97a42e7b 100644
--- a/internal/cmd/operator-sdk/bundle/validate/cmd.go
+++ b/internal/cmd/operator-sdk/bundle/validate/cmd.go
@@ -65,17 +65,32 @@ To build and validate a *pullable* bundle image:
 To list and run optional validators, which are specified by a label selector:
 
   $ operator-sdk bundle validate --list-optional
-  NAME           LABELS                     DESCRIPTION
-  operatorhub    name=operatorhub           OperatorHub.io metadata validation
-                 suite=operatorframework
+  NAME           LABELS                                                DESCRIPTION
+  operatorhub    name=operatorhub                                      OperatorHub.io metadata validation. 
+                 suite=operatorframework    
+  community      name=community                                        (stage: alpha) Community Operator bundle validation.
 
 To validate a bundle against the entire suite of validators for Operator Framework, in addition to required bundle validators:
 	
   $ operator-sdk bundle validate ./bundle --select-optional suite=operatorframework
 
+**NOTE:** The OperatorHub.io validator in the operatorframework optional suite allows you to validate that your manifests can work with a Kubernetes cluster of a particular version using the k8s-version optional key value:	
+
+  $ operator-sdk bundle validate ./bundle --select-optional suite=operatorframework --optional-values=k8s-version=1.22
+	
 To validate a bundle against the validator for operatorhub.io specifically, in addition to required bundle validators:
 	
   $ operator-sdk bundle validate ./bundle --select-optional name=operatorhub
+	
+**NOTE:** This validator allows check the bundle against an specific Kubernetes cluster version using the k8s-version optional key value:
+	
+  $ operator-sdk bundle validate ./bundle --select-optional name=operatorhub --optional-values=k8s-version=1.22
+
+(stage: alpha) To validate a bundle against the validator for Community Operators specifically, in addition to required bundle validators. This validator allows check the bundle image. In this way, inform the path-index optional key value:
+	
+  $ operator-sdk bundle validate ./bundle --select-optional name=community --optional-values=index-path=bundle.Dockerfile
+	
+**NOTE:** Community Operators are the operators which will be distributed on the OKD/OCP catalogs. More info: https://github.com/operator-framework/community-operators/
 `
 )
 
diff --git a/internal/cmd/operator-sdk/bundle/validate/optional.go b/internal/cmd/operator-sdk/bundle/validate/optional.go
index c87c165c..4fc2f8ea 100644
--- a/internal/cmd/operator-sdk/bundle/validate/optional.go
+++ b/internal/cmd/operator-sdk/bundle/validate/optional.go
@@ -42,7 +42,15 @@ var optionalValidators = validators{
 			nameKey:  "operatorhub",
 			suiteKey: "operatorframework",
 		},
-		desc: "OperatorHub.io metadata validation",
+		desc: "OperatorHub.io metadata validation. ",
+	},
+	{
+		Validator: apivalidation.CommunityOperatorValidator,
+		name:      "community",
+		labels: map[string]string{
+			nameKey: "community",
+		},
+		desc: "(stage: alpha) Community Operator bundle validation.",
 	},
 }
 
@@ -117,6 +125,7 @@ func (vals validators) run(bundle *apimanifests.Bundle, sel labels.Selector, opt
 	}
 
 	// Pass the --optional-values. e.g. --optional-values="k8s-version=1.22"
+	// or --optional-values="image-path=bundle.Dockerfile"
 	objs = append(objs, optionalValues)
 
 	for _, v := range vals {
diff --git a/internal/helm/release/manager.go b/internal/helm/release/manager.go
index 2d494f42..9489d377 100644
--- a/internal/helm/release/manager.go
+++ b/internal/helm/release/manager.go
@@ -32,6 +32,7 @@ import (
 	"helm.sh/helm/v3/pkg/storage/driver"
 	apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
+	apiequality "k8s.io/apimachinery/pkg/api/equality"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -125,14 +126,7 @@ func (m *manager) Sync(ctx context.Context) error {
 	m.deployedRelease = deployedRelease
 	m.isInstalled = true
 
-	// Get the next candidate release to determine if an upgrade is necessary.
-	candidateRelease, err := m.getCandidateRelease(m.namespace, m.releaseName, m.chart, m.values)
-	if err != nil {
-		return fmt.Errorf("failed to get candidate release: %w", err)
-	}
-	if deployedRelease.Manifest != candidateRelease.Manifest {
-		m.isUpgradeRequired = true
-	}
+	m.isUpgradeRequired = m.isUpgrade(deployedRelease)
 
 	return nil
 }
@@ -141,6 +135,20 @@ func notFoundErr(err error) bool {
 	return err != nil && strings.Contains(err.Error(), "not found")
 }
 
+func (m manager) isUpgrade(deployedRelease *rpb.Release) bool {
+	if deployedRelease == nil {
+		return false
+	}
+
+	// Judging whether to skip updates
+	skip := m.namespace == deployedRelease.Namespace
+	skip = skip && m.releaseName == deployedRelease.Name
+	skip = skip && apiequality.Semantic.DeepEqual(m.chart, deployedRelease.Chart)
+	skip = skip && apiequality.Semantic.DeepEqual(m.values, deployedRelease.Config)
+
+	return !skip
+}
+
 func (m manager) getDeployedRelease() (*rpb.Release, error) {
 	deployedRelease, err := m.storageBackend.Deployed(m.releaseName)
 	if err != nil {
@@ -152,14 +160,6 @@ func (m manager) getDeployedRelease() (*rpb.Release, error) {
 	return deployedRelease, nil
 }
 
-func (m manager) getCandidateRelease(namespace, name string, chart *cpb.Chart,
-	values map[string]interface{}) (*rpb.Release, error) {
-	upgrade := action.NewUpgrade(m.actionConfig)
-	upgrade.Namespace = namespace
-	upgrade.DryRun = true
-	return upgrade.Run(name, chart, values)
-}
-
 // InstallRelease performs a Helm release install.
 func (m manager) InstallRelease(ctx context.Context, opts ...InstallOption) (*rpb.Release, error) {
 	install := action.NewInstall(m.actionConfig)
diff --git a/website/content/en/docs/cli/operator-sdk_bundle_validate.md b/website/content/en/docs/cli/operator-sdk_bundle_validate.md
index 066a019a..979d7852 100644
--- a/website/content/en/docs/cli/operator-sdk_bundle_validate.md
+++ b/website/content/en/docs/cli/operator-sdk_bundle_validate.md
@@ -51,17 +51,32 @@ To build and validate a *pullable* bundle image:
 To list and run optional validators, which are specified by a label selector:
 
   $ operator-sdk bundle validate --list-optional
-  NAME           LABELS                     DESCRIPTION
-  operatorhub    name=operatorhub           OperatorHub.io metadata validation
-                 suite=operatorframework
+  NAME           LABELS                                                DESCRIPTION
+  operatorhub    name=operatorhub                                      OperatorHub.io metadata validation. 
+                 suite=operatorframework    
+  community      name=community                                        (stage: alpha) Community Operator bundle validation.
 
 To validate a bundle against the entire suite of validators for Operator Framework, in addition to required bundle validators:
 	
   $ operator-sdk bundle validate ./bundle --select-optional suite=operatorframework
 
+**NOTE:** The OperatorHub.io validator in the operatorframework optional suite allows you to validate that your manifests can work with a Kubernetes cluster of a particular version using the k8s-version optional key value:	
+
+  $ operator-sdk bundle validate ./bundle --select-optional suite=operatorframework --optional-values=k8s-version=1.22
+	
 To validate a bundle against the validator for operatorhub.io specifically, in addition to required bundle validators:
 	
   $ operator-sdk bundle validate ./bundle --select-optional name=operatorhub
+	
+**NOTE:** This validator allows check the bundle against an specific Kubernetes cluster version using the k8s-version optional key value:
+	
+  $ operator-sdk bundle validate ./bundle --select-optional name=operatorhub --optional-values=k8s-version=1.22
+
+(stage: alpha) To validate a bundle against the validator for Community Operators specifically, in addition to required bundle validators. This validator allows check the bundle image. In this way, inform the path-index optional key value:
+	
+  $ operator-sdk bundle validate ./bundle --select-optional name=community --optional-values=index-path=bundle.Dockerfile
+	
+**NOTE:** Community Operators are the operators which will be distributed on the OKD/OCP catalogs. More info: https://github.com/operator-framework/community-operators/
 
 ```
 
diff --git a/website/content/en/docs/contribution-guidelines/releasing.md b/website/content/en/docs/contribution-guidelines/releasing.md
index 0a596c18..d290cc17 100644
--- a/website/content/en/docs/contribution-guidelines/releasing.md
+++ b/website/content/en/docs/contribution-guidelines/releasing.md
@@ -45,7 +45,7 @@ assuming the upstream SDK is the `upstream` remote repo:
   git checkout -b v1.3.x
   git push -u upstream v1.3.x
   ```
-1. Make sure that the list of supported OLM versions stated in the [Overview][overview] section of SDK docs is updated. If a new version of OLM needs to be officially supported, follow the steps in [updating OLM bindata](#updating-olm-bindata) section. 
+1. Make sure that the list of supported OLM versions stated in the [Overview][overview] section of SDK docs is updated. If a new version of OLM needs to be officially supported, follow the steps in [updating OLM bindata](#updating-olm-bindata) section.
 1. Create and merge a commit that updates the top-level [Makefile] variable `IMAGE_VERSION`
 to the upcoming release tag `v1.3.0`. This variable ensures sample projects have been tagged
 correctly prior to the release commit.
@@ -135,23 +135,18 @@ git push -f upstream v1.3.x
 
 We will use the `v1.3.1` release version in this example.
 
-### Before starting
+#### 0. Lock down release branches on GitHub
 
-1. Create and merge a commit that updates the top-level [Makefile] variable `IMAGE_VERSION`
-to the upcoming release tag `v1.3.1`. This variable ensures sample projects have been tagged
-correctly prior to the release commit.
-  ```sh
-  sed -i -E 's/(IMAGE_VERSION = ).+/\1v1\.3\.1/g' Makefile
-  ```
-1. Lock down the `v1.3.x` branch to prevent further commits before the release completes:
-  1. Go to `Settings -> Branches` in the SDK repo.
-  1. Under `Branch protection rules`, click `Edit` on the `v.*` branch rule.
-  1. In section `Protect matching branches` of the `Rule settings` box, increase the number of required approving reviewers to 6.
+Lock down the `v1.3.x` branch to prevent further merges/commits.
 
-### 1. Create and push a release commit
+To do this, edit the `Branch protection rules`: https://github.com/operator-framework/operator-sdk/settings/branches
+
+- click `Edit` on the `v.*` branch rule.
+- In section `Protect matching branches` of the `Rule settings` box, set "Required approving reviewers" to `6`.
 
-Create a new branch from the release branch, which should already exist for the desired minor version,
-to push the release commit to:
+#### 1. Branch
+
+Create a new branch from the release branch (v1.3.x in this example). This branch should already exist prior to cutting a patch release.
 
 ```sh
 export RELEASE_VERSION=v1.3.1
@@ -160,45 +155,61 @@ git pull
 git checkout -b release-$RELEASE_VERSION
 ```
 
-Run the pre-release `make` target:
 
-```sh
+#### 2. Prepare the release commit
+
+Using the version for your release as the IMAGE_VERSION, execute the
+following commands from the root of the project.
+
+```sh 
+# Update the IMAGE_VERSION in the Makefile 
+sed -i -E 's/(IMAGE_VERSION = ).+/\1v1\.3\.1/g' Makefile
+#  Run the pre-release `make` target:
 make prerelease
+# Regenerate testdata (samples). 
+# NOTE: The sanity test will fail but scaffolding should complete.
+make test-sanity
 ```
 
-The following changes should be present:
+All of the following changes should be present (and no others).
 
-- `changelog/generated/v1.3.0.md`: commit changes (created by changelog generation).
-- `changelog/fragments/*`: commit deleted fragment files (deleted by changelog generation).
+- Makefile: IMAGE_VERSION should be modified to the upcoming release tag. (This variable ensures sampleprojects have been tagged correctpy priror to the release commit.)
+- changelog/: all fragments should be deleted and consolidated into the new file `changelog/generated/v1.3.1.md`
+- docs: If there are migration steps, a new migration doc will be created. The installation docs should also contain a link update.
+- testdata/: Generated samples and tests should have version bumps
 
-Commit these changes and push:
+Commit these changes and push these changes **to your fork**:
 
 ```sh
 git add --all
-git commit -m "Release $RELEASE_VERSION"
+git commit -sm "Release $RELEASE_VERSION"
 git push -u origin release-$RELEASE_VERSION
 ```
 
-### 2. Create and merge a new PR
+#### 3. Create and merge Pull Request
 
-Create and merge a new PR for the commit created in step 1. You can force-merge your PR to the locked-down `v1.3.x`
-if you have admin access to the operator-sdk repo, or ask an administrator to do so.
+- Create a pull request against the `v1.3.x` branch. 
+- Once approving review is given, merge. You may have to unlock the branch by setting
+"required approving reviewers" to back to `1`. (See step 0).
 
-### 3. Unlock the `v1.3.x` branch
+#### 4. Create a release tag
 
-Unlock the branch by changing the number of required approving reviewers in the `v.*` branch rule back to 1.
-
-### 4. Create and push a release tag
+Pull down `v1.3.x` and tag it.
 
 ```sh
+git checkout v1.3.x
+git pull upstream v1.3.x
 make tag
 git push upstream refs/tags/$RELEASE_VERSION
 ```
 
 ### 5. Fast-forward the `latest` branch
 
-The `latest` branch points to the latest release tag to keep the main website subdomain up-to-date.
-Run the following commands to do so:
+If the patch release is on the latest y-stream (in the example you would
+not ff latest if there was a y-stream for v1.4.x), you will need to
+fast-forward the `latest` git branch. 
+
+(The `latest` branch points to the latest release tag to keep the main website subdomain up-to-date.)
 
 ```sh
 git checkout latest
@@ -272,7 +283,7 @@ GitHub releases live under the [`Releases` tab][release-page] in the operator-sd
 
 Prior to an Operator SDK release, add bindata (if required) for a new OLM version by following these steps:
 
-1. Add the new version to the [`OLM_VERSIONS`][olm_version] variable in the Makefile. 
+1. Add the new version to the [`OLM_VERSIONS`][olm_version] variable in the Makefile.
 2. Remove the *lowest* version from that variable, as `operator-sdk` only supports 3 versions at a time.
 3. Run `make bindata`.
 4. Update the list of supported OLM versions stated in the [`Overview`][overview] section of SDK documentation is updated.
diff --git a/website/content/en/docs/olm-integration/generation.md b/website/content/en/docs/olm-integration/generation.md
index 119ea28b..b9cec120 100644
--- a/website/content/en/docs/olm-integration/generation.md
+++ b/website/content/en/docs/olm-integration/generation.md
@@ -223,9 +223,10 @@ You can list all available optional validators by setting the `--list-optional`
 
 ```console
 $ operator-sdk bundle validate --list-optional
-NAME           LABELS                     DESCRIPTION
-operatorhub    name=operatorhub           OperatorHub.io metadata validation
-               suite=operatorframework
+NAME           LABELS                                                DESCRIPTION
+operatorhub    name=operatorhub                                      OperatorHub.io metadata validation. 
+               suite=operatorframework    
+community      name=community                                        (stage: alpha) Community Operator bundle validation      
 ...
 ```
 
@@ -254,6 +255,12 @@ operator-sdk bundle validate ./bundle --select-optional suite=operatorframework
 Documentation on optional validators:
 - [`operatorhub`][operatorhub_validator]
 
+**Note**: (stage: alpha) The `Community` validator allows you to validate your `bundle.Dockerfile` configuration against its specific criteria using the `image-path` optional key value:
+
+```sh 
+operator-sdk bundle validate ./bundle --select-optional name=community --optional-values=image-path=bundle.Dockerfile
+```
+
 ### Package manifests format
 
 A [package manifests][package-manifests] format consists of on-disk manifests (CSV, CRDs and other supported kinds)
diff --git a/website/scripts/update_download_url.sh b/website/scripts/update_download_url.sh
index 2919de91..17413ebb 100755
--- a/website/scripts/update_download_url.sh
+++ b/website/scripts/update_download_url.sh
@@ -10,7 +10,7 @@ DOC_PATH="${DIR}/../content/en/docs/installation/_index.md"
 
 VERSION="${1?"A Version is required"}"
 
-TARGET="export OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/"
+TARGET="export OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/"
 
 sed -i -E 's@('"${TARGET}"').+@\1'"${VERSION}"'@g' "$DOC_PATH"
 


Test patch:
diff --git a/internal/helm/release/manager_test.go b/internal/helm/release/manager_test.go
index a7105dd7..77ccca43 100644
--- a/internal/helm/release/manager_test.go
+++ b/internal/helm/release/manager_test.go
@@ -17,16 +17,17 @@ package release
 import (
 	"testing"
 
-	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	apitypes "k8s.io/apimachinery/pkg/types"
-	"k8s.io/cli-runtime/pkg/resource"
-
+	"github.com/stretchr/testify/assert"
+	cpb "helm.sh/helm/v3/pkg/chart"
+	lpb "helm.sh/helm/v3/pkg/chart/loader"
+	rpb "helm.sh/helm/v3/pkg/release"
 	appsv1 "k8s.io/api/apps/v1"
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	"github.com/stretchr/testify/assert"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
+	apitypes "k8s.io/apimachinery/pkg/types"
+	"k8s.io/cli-runtime/pkg/resource"
 )
 
 func newTestUnstructured(containers []interface{}) *unstructured.Unstructured {
@@ -213,3 +214,72 @@ func TestManagerGenerateStrategicMergePatch(t *testing.T) {
 		assert.Equal(t, test.patch, string(diff))
 	}
 }
+
+func TestManagerisUpgrade(t *testing.T) {
+	tests := []struct {
+		name            string
+		releaseName     string
+		releaseNs       string
+		values          map[string]interface{}
+		chart           *cpb.Chart
+		deployedRelease *rpb.Release
+		want            bool
+	}{
+		{
+			name:            "ok",
+			releaseName:     "deployed",
+			releaseNs:       "deployed-ns",
+			values:          map[string]interface{}{"key": "value"},
+			chart:           newTestChart(t, "./testdata/simple"),
+			deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{"key": "value"}, "deployed", "deployed-ns"),
+			want:            false,
+		},
+		{
+			name:            "different chart",
+			releaseName:     "deployed",
+			releaseNs:       "deployed-ns",
+			values:          map[string]interface{}{"key": "value"},
+			chart:           newTestChart(t, "./testdata/simple"),
+			deployedRelease: newTestRelease(newTestChart(t, "./testdata/simpledf"), map[string]interface{}{"key": "value"}, "deployed", "deployed-ns"),
+			want:            true,
+		},
+		{
+			name:            "different values",
+			releaseName:     "deployed",
+			releaseNs:       "deployed-ns",
+			values:          map[string]interface{}{"key": "1"},
+			chart:           newTestChart(t, "./testdata/simple"),
+			deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{"key": ""}, "deployed", "deployed-ns"),
+			want:            true,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			m := manager{
+				releaseName: test.releaseName,
+				namespace:   test.releaseNs,
+				values:      test.values,
+				chart:       test.chart,
+			}
+			isUpgrade := m.isUpgrade(test.deployedRelease)
+			assert.Equal(t, test.want, isUpgrade)
+		})
+	}
+}
+
+func newTestChart(t *testing.T, path string) *cpb.Chart {
+	chart, err := lpb.Load(path)
+	assert.Nil(t, err)
+	return chart
+}
+
+func newTestRelease(chart *cpb.Chart, values map[string]interface{}, name, namespace string) *rpb.Release {
+	release := rpb.Mock(&rpb.MockReleaseOptions{
+		Name:      name,
+		Namespace: namespace,
+		Chart:     chart,
+		Version:   1,
+	})
+	release.Config = values
+	return release
+}
diff --git a/internal/helm/release/testdata/simple/.helmignore b/internal/helm/release/testdata/simple/.helmignore
new file mode 100644
index 00000000..f0c13194
--- /dev/null
+++ b/internal/helm/release/testdata/simple/.helmignore
@@ -0,0 +1,21 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
diff --git a/internal/helm/release/testdata/simple/Chart.yaml b/internal/helm/release/testdata/simple/Chart.yaml
new file mode 100644
index 00000000..786c7721
--- /dev/null
+++ b/internal/helm/release/testdata/simple/Chart.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+description: a simple charts
+name: simple
+keywords:
+  - helm
+  - operator-sdk
+version: 0.0.1+master
+appVersion: "master"
diff --git a/internal/helm/release/testdata/simple/templates/NOTES.txt b/internal/helm/release/testdata/simple/templates/NOTES.txt
new file mode 100644
index 00000000..da930300
--- /dev/null
+++ b/internal/helm/release/testdata/simple/templates/NOTES.txt
@@ -0,0 +1,1 @@
+A simple charts
\ No newline at end of file
diff --git a/internal/helm/release/testdata/simple/templates/_helpers.tpl b/internal/helm/release/testdata/simple/templates/_helpers.tpl
new file mode 100644
index 00000000..e5fe9272
--- /dev/null
+++ b/internal/helm/release/testdata/simple/templates/_helpers.tpl
@@ -0,0 +1,7 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "simple.fullname" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
diff --git a/internal/helm/release/testdata/simple/templates/secrets.yaml b/internal/helm/release/testdata/simple/templates/secrets.yaml
new file mode 100644
index 00000000..4b61fbb7
--- /dev/null
+++ b/internal/helm/release/testdata/simple/templates/secrets.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ template "simple.fullname" . }}
+  namespace: {{ .Release.Namespace }}
+type: Opaque
+data:
+  password: {{ randAlphaNum 10 | b64enc | quote }}
\ No newline at end of file
diff --git a/internal/helm/release/testdata/simple/values.yaml b/internal/helm/release/testdata/simple/values.yaml
new file mode 100644
index 00000000..ca762273
--- /dev/null
+++ b/internal/helm/release/testdata/simple/values.yaml
@@ -0,0 +1,1 @@
+# simple values
\ No newline at end of file
diff --git a/internal/helm/release/testdata/simpledf/.helmignore b/internal/helm/release/testdata/simpledf/.helmignore
new file mode 100644
index 00000000..f0c13194
--- /dev/null
+++ b/internal/helm/release/testdata/simpledf/.helmignore
@@ -0,0 +1,21 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
diff --git a/internal/helm/release/testdata/simpledf/Chart.yaml b/internal/helm/release/testdata/simpledf/Chart.yaml
new file mode 100644
index 00000000..786c7721
--- /dev/null
+++ b/internal/helm/release/testdata/simpledf/Chart.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+description: a simple charts
+name: simple
+keywords:
+  - helm
+  - operator-sdk
+version: 0.0.1+master
+appVersion: "master"
diff --git a/internal/helm/release/testdata/simpledf/templates/NOTES.txt b/internal/helm/release/testdata/simpledf/templates/NOTES.txt
new file mode 100644
index 00000000..da930300
--- /dev/null
+++ b/internal/helm/release/testdata/simpledf/templates/NOTES.txt
@@ -0,0 +1,1 @@
+A simple charts
\ No newline at end of file
diff --git a/internal/helm/release/testdata/simpledf/templates/_helpers.tpl b/internal/helm/release/testdata/simpledf/templates/_helpers.tpl
new file mode 100644
index 00000000..e5fe9272
--- /dev/null
+++ b/internal/helm/release/testdata/simpledf/templates/_helpers.tpl
@@ -0,0 +1,7 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "simple.fullname" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
diff --git a/internal/helm/release/testdata/simpledf/templates/secrets.yaml b/internal/helm/release/testdata/simpledf/templates/secrets.yaml
new file mode 100644
index 00000000..4b61fbb7
--- /dev/null
+++ b/internal/helm/release/testdata/simpledf/templates/secrets.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ template "simple.fullname" . }}
+  namespace: {{ .Release.Namespace }}
+type: Opaque
+data:
+  password: {{ randAlphaNum 10 | b64enc | quote }}
\ No newline at end of file
diff --git a/internal/helm/release/testdata/simpledf/values.yaml b/internal/helm/release/testdata/simpledf/values.yaml
new file mode 100644
index 00000000..a99c72dc
--- /dev/null
+++ b/internal/helm/release/testdata/simpledf/values.yaml
@@ -0,0 +1,2 @@
+# simple values
+name: simpledf
\ No newline at end of file