Repository: argoproj-labs/argo-rollouts-manager
Pull request #40 (2024-02-15 10:38:05)

Add ginkgo unit tests for configMaps
**What does this PR do / why we need it**:
- Ginkgo tests for configMaps
- minor updates in other test files

**Which issue(s) this PR fixes**:
Fixes #? Issue #22  and Issue #25 and #43




Patch:
diff --git a/Makefile b/Makefile
index 40bd59a..fe0029c 100644
--- a/Makefile
+++ b/Makefile
@@ -283,7 +283,7 @@ lint: golangci_lint
 
 .PHONY: gosec
 gosec: go_sec
-	$(GO_SEC) -exclude-dir=hack ./...
+	$(GO_SEC) ./...
 
 # go-get-tool will 'go install' any package $2 and install it to $1.
 PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
diff --git a/controllers/argorollouts_controller.go b/controllers/argorollouts_controller.go
index 48e4fc8..3021d24 100644
--- a/controllers/argorollouts_controller.go
+++ b/controllers/argorollouts_controller.go
@@ -37,7 +37,8 @@ var _ reconcile.Reconciler = &RolloutManagerReconciler{}
 // RolloutManagerReconciler reconciles a RolloutManager object
 type RolloutManagerReconciler struct {
 	client.Client
-	Scheme *runtime.Scheme
+	Scheme                       *runtime.Scheme
+	OpenShiftRoutePluginLocation string
 }
 
 var log = logr.Log.WithName("rollouts-controller")
diff --git a/controllers/configmap.go b/controllers/configmap.go
index 50c40a8..70e602b 100644
--- a/controllers/configmap.go
+++ b/controllers/configmap.go
@@ -12,11 +12,16 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-// TODO: #22 - Remove this once ConfigMap reconciliation is fixed:
-// nolint:unused
+// From https://argo-rollouts.readthedocs.io/en/stable/features/traffic-management/plugins/
+const TrafficRouterPluginConfigMapKey = "trafficRouterPlugins"
+
 // Reconcile the Rollouts Default Config Map.
 func (r *RolloutManagerReconciler) reconcileConfigMap(ctx context.Context, cr *rolloutsmanagerv1alpha1.RolloutManager) error {
 
+	if r.OpenShiftRoutePluginLocation == "" { // sanity test the plugin value
+		return fmt.Errorf("OpenShift Route Plugin location is not set")
+	}
+
 	desiredConfigMap := &corev1.ConfigMap{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      DefaultRolloutsConfigMapName,
@@ -29,7 +34,7 @@ func (r *RolloutManagerReconciler) reconcileConfigMap(ctx context.Context, cr *r
 	trafficRouterPlugins := []types.PluginItem{
 		{
 			Name:     OpenShiftRolloutPluginName,
-			Location: "file://" + OpenShiftRolloutPluginPath,
+			Location: r.OpenShiftRoutePluginLocation,
 		},
 	}
 	pluginString, err := yaml.Marshal(trafficRouterPlugins)
@@ -37,7 +42,7 @@ func (r *RolloutManagerReconciler) reconcileConfigMap(ctx context.Context, cr *r
 		return fmt.Errorf("error marshalling trafficRouterPlugin to string %s", err)
 	}
 	desiredConfigMap.Data = map[string]string{
-		"trafficRouterPlugins": string(pluginString),
+		TrafficRouterPluginConfigMapKey: string(pluginString),
 	}
 
 	actualConfigMap := &corev1.ConfigMap{}
@@ -52,7 +57,7 @@ func (r *RolloutManagerReconciler) reconcileConfigMap(ctx context.Context, cr *r
 	}
 
 	var actualTrafficRouterPlugins []types.PluginItem
-	if err = yaml.Unmarshal([]byte(actualConfigMap.Data["trafficRouterPlugins"]), &actualTrafficRouterPlugins); err != nil {
+	if err = yaml.Unmarshal([]byte(actualConfigMap.Data[TrafficRouterPluginConfigMapKey]), &actualTrafficRouterPlugins); err != nil {
 		return fmt.Errorf("failed to unmarshal traffic router plugins from ConfigMap: %s", err)
 	}
 
@@ -70,7 +75,7 @@ func (r *RolloutManagerReconciler) reconcileConfigMap(ctx context.Context, cr *r
 		return fmt.Errorf("error marshalling trafficRouterPlugin to string %s", err)
 	}
 
-	actualConfigMap.Data["trafficRouterPlugins"] = string(pluginString)
+	actualConfigMap.Data[TrafficRouterPluginConfigMapKey] = string(pluginString)
 
 	return r.Client.Update(ctx, actualConfigMap)
 }
diff --git a/controllers/default.go b/controllers/default.go
index ad6a597..4038980 100644
--- a/controllers/default.go
+++ b/controllers/default.go
@@ -19,9 +19,8 @@ const (
 	DefaultRolloutsSelectorKey = "app.kubernetes.io/name"
 
 	// OpenShiftRolloutPluginName is the plugin name for Openshift Route Plugin
-	OpenShiftRolloutPluginName = "openshift-route-plugin"
-	// OpenShiftRolloutPluginPath is the path on the rollout controller pod where the plugin will be mounted
-	OpenShiftRolloutPluginPath = "/plugin/rollouts-plugin-trafficrouter-openshift"
+	OpenShiftRolloutPluginName = "argoproj-labs/openshift-route-plugin"
+
 	// DefaultRolloutsConfigMapName is the default name of the ConfigMap that contains the Rollouts controller configuration
 	DefaultRolloutsConfigMapName = "argo-rollouts-config"
 )
diff --git a/controllers/deployment.go b/controllers/deployment.go
index e1f5463..5987f75 100644
--- a/controllers/deployment.go
+++ b/controllers/deployment.go
@@ -24,6 +24,7 @@ func (r *RolloutManagerReconciler) reconcileRolloutsDeployment(ctx context.Conte
 			Namespace: cr.Namespace,
 		},
 	}
+
 	setRolloutsLabels(&desiredDeployment.ObjectMeta)
 
 	desiredDeployment.Spec = appsv1.DeploymentSpec{
@@ -65,8 +66,15 @@ func (r *RolloutManagerReconciler) reconcileRolloutsDeployment(ctx context.Conte
 		rolloutsContainer(cr),
 	}
 
+	desiredPodSpec.Volumes = []corev1.Volume{
+		{
+			Name: "tmp",
+		},
+	}
+
 	// If the deployment for rollouts does not exist, create one.
 	actualDeployment := &appsv1.Deployment{}
+
 	if err := fetchObject(ctx, r.Client, cr.Namespace, DefaultArgoRolloutsResourceName, actualDeployment); err != nil {
 		if !errors.IsNotFound(err) {
 			return fmt.Errorf("failed to get the deployment %s : %s", DefaultArgoRolloutsResourceName, err)
@@ -82,7 +90,7 @@ func (r *RolloutManagerReconciler) reconcileRolloutsDeployment(ctx context.Conte
 	actualPodSpec := actualDeployment.Spec.Template.Spec
 
 	// If the Deployment already exists, make sure the values we care about are correct.
-	deploymentsDifferent := !reflect.DeepEqual(actualPodSpec.Containers[0], desiredPodSpec.Containers) ||
+	deploymentsDifferent := !reflect.DeepEqual(actualPodSpec.Containers, desiredPodSpec.Containers) ||
 		actualPodSpec.ServiceAccountName != desiredPodSpec.ServiceAccountName ||
 		!reflect.DeepEqual(actualDeployment.Labels, desiredDeployment.Labels) ||
 		!reflect.DeepEqual(actualDeployment.Spec.Template.Labels, desiredDeployment.Spec.Template.Labels) ||
@@ -167,6 +175,12 @@ func rolloutsContainer(cr *rolloutsmanagerv1alpha1.RolloutManager) corev1.Contai
 			ReadOnlyRootFilesystem:   boolPtr(false),
 			RunAsNonRoot:             boolPtr(true),
 		},
+		VolumeMounts: []corev1.VolumeMount{
+			{
+				MountPath: "/home/argo-rollouts/plugin-bin",
+				Name:      "tmp",
+			},
+		},
 	}
 
 }
diff --git a/controllers/reconcile.go b/controllers/reconcile.go
index fb87785..6584504 100644
--- a/controllers/reconcile.go
+++ b/controllers/reconcile.go
@@ -45,13 +45,11 @@ func (r *RolloutManagerReconciler) reconcileRolloutsManager(ctx context.Context,
 		return err
 	}
 
-	// TODO: #22 - Re-enable this once ConfigMap reconciliation is fixed:
-
-	// // reconcile configMap for plugins
-	// log.Info("reconciling configMap for plugins")
-	// if err := r.reconcileConfigMap(ctx, cr); err != nil {
-	// 	return err
-	// }
+	// reconcile configMap for plugins
+	log.Info("reconciling configMap for plugins")
+	if err := r.reconcileConfigMap(ctx, cr); err != nil {
+		return err
+	}
 
 	log.Info("reconciling rollouts deployment")
 	if err := r.reconcileRolloutsDeployment(ctx, cr, sa); err != nil {
diff --git a/hack/upgrade-rollouts-script/.gitignore b/hack/upgrade-rollouts-script/.gitignore
deleted file mode 100644
index 2fd7cf2..0000000
--- a/hack/upgrade-rollouts-script/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-settings.env
-argo-rollouts-manager
diff --git a/hack/upgrade-rollouts-script/README.md b/hack/upgrade-rollouts-script/README.md
deleted file mode 100644
index 16cc5f5..0000000
--- a/hack/upgrade-rollouts-script/README.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Update argo-rollouts-manager to latest release of Argo Rollouts
-
-The Go code and script this in this directory will automatically open a pull request to update the argo-rollouts-manager to the latest official argo-rollouts release:
-- Update container image version in `default.go`
-- Update `go.mod` to point to latest module version
-- Update CRDs to latest
-- Update target Rollouts version in `hack/run-upstream-argo-rollouts-e2e-tests.sh`
-- Open Pull Request using 'gh' CLI
-
-## Instructions
-
-### Prerequisites
-- GitHub CLI (_gh_) installed and on PATH
-- Go installed an on PATH
-- [Operator-sdk v1.28.0](https://github.com/operator-framework/operator-sdk/releases/tag/v1.28.0) installed (as of January 2024), and on PATH
-- You must have your own fork of the [argo-rollouts-manager](https://github.com/argoproj-labs/argo-rollouts-manager) repository (example: `jgwest/argo-rollouts-manager`)
-- Your local SSH key registered (e.g. `~/.ssh/id_rsa.pub`) with GitHub to allow git clone via SSH
-
-### Configure and run the tool
-
-```bash
-
-# Set required Environment Variables
-export GITHUB_FORK_USERNAME="(your username here)"
-export GH_TOKEN="(a GitHub personal access token that can clone/push/open PRs against argo-rollouts-manager repo)"
-
-# or, you can set these values in the settings.env file:
-#   cp settings_template.env settings.env
-# Then set env vars in settings.env (which is excluded in the .gitignore)
-
-./init-repo.sh
-./go-run.sh
-```
diff --git a/hack/upgrade-rollouts-script/go-run.sh b/hack/upgrade-rollouts-script/go-run.sh
deleted file mode 100755
index ac34a6a..0000000
--- a/hack/upgrade-rollouts-script/go-run.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
-
-cd $SCRIPT_DIR
-
-# Read Github Token and Username from settings.env, if it exists
-vars_file="$SCRIPT_DIR/settings.env"
-if [[ -f "$vars_file" ]]; then
-    source "$vars_file"
-fi
-
-# Run the upgrade code
-go run .
diff --git a/hack/upgrade-rollouts-script/go.mod b/hack/upgrade-rollouts-script/go.mod
deleted file mode 100644
index 7d511ef..0000000
--- a/hack/upgrade-rollouts-script/go.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-module github.com/jgwest/argo-rollouts-release-job
-
-go 1.20
-
-require (
-	github.com/google/go-github/v58 v58.0.0
-	github.com/google/go-querystring v1.1.0 // indirect
-)
diff --git a/hack/upgrade-rollouts-script/go.sum b/hack/upgrade-rollouts-script/go.sum
deleted file mode 100644
index 703631d..0000000
--- a/hack/upgrade-rollouts-script/go.sum
+++ /dev/null
@@ -1,7 +0,0 @@
-github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw=
-github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4=
-github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
-github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/hack/upgrade-rollouts-script/init-repo.sh b/hack/upgrade-rollouts-script/init-repo.sh
deleted file mode 100755
index 572512b..0000000
--- a/hack/upgrade-rollouts-script/init-repo.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-
-SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
-
-cd $SCRIPT_DIR
-
-# Read Github Token and Username from settings.env, if it exists
-vars_file="$SCRIPT_DIR/settings.env"
-if [[ -f "$vars_file" ]]; then
-    source "$vars_file"
-fi
-
-# Clone fork of argo-rollouts-manager repo
-
-rm -rf "$SCRIPT_DIR/argo-rollouts-manager" || true
-
-git clone "git@github.com:$GITHUB_FORK_USERNAME/argo-rollouts-manager"
-cd argo-rollouts-manager
-
-# Add a remote back to the original repo
-
-git remote add parent "git@github.com:argoproj-labs/argo-rollouts-manager"
-git fetch parent
-
diff --git a/hack/upgrade-rollouts-script/main.go b/hack/upgrade-rollouts-script/main.go
deleted file mode 100644
index 051e553..0000000
--- a/hack/upgrade-rollouts-script/main.go
+++ /dev/null
@@ -1,447 +0,0 @@
-package main
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"io"
-	"os"
-	"path/filepath"
-	"reflect"
-	"sort"
-	"strings"
-
-	"os/exec"
-
-	"github.com/google/go-github/v58/github"
-)
-
-// These can be set while debugging
-const (
-	skipInitialPRCheck = false // default to false
-
-	// if readOnly is true:
-	// - PRs will not be opened
-	// - Git commits will not be pushed to fork
-	// This is roughly equivalent to a dry run
-	readOnly = false // default to false
-
-)
-
-const (
-	PRTitle              = "Upgrade to Argo Rollouts "
-	argoRolloutsRepoOrg  = "argoproj"
-	argoRolloutsRepoName = "argo-rollouts"
-
-	argoprojlabsRepoOrg         = "argoproj-labs"
-	argoRolloutsManagerRepoName = "argo-rollouts-manager"
-
-	controllersDefaultGo = "controllers/default.go"
-)
-
-func main() {
-
-	pathToGitHubRepo := "argo-rollouts-manager"
-
-	gitHubToken := os.Getenv("GH_TOKEN")
-	if gitHubToken == "" {
-		exitWithError(fmt.Errorf("missing GH_TOKEN"))
-		return
-	}
-
-	client := github.NewClient(nil).WithAuthToken(gitHubToken)
-
-	// 1) Check for existing version update PRs on the repo
-
-	if !skipInitialPRCheck {
-		prList, _, err := client.PullRequests.List(context.Background(), argoprojlabsRepoOrg, argoRolloutsManagerRepoName, &github.PullRequestListOptions{})
-		if err != nil {
-			exitWithError(err)
-			return
-		}
-		for _, pr := range prList {
-			if strings.HasPrefix(*pr.Title, PRTitle) {
-				exitWithError(fmt.Errorf("PR already exists"))
-				return
-			}
-		}
-	}
-
-	// 2) Pull the latest releases from rollouts repo
-
-	releases, _, err := client.Repositories.ListReleases(context.Background(), argoRolloutsRepoOrg, argoRolloutsRepoName, &github.ListOptions{})
-	if err != nil {
-		exitWithError(err)
-		return
-	}
-
-	var firstProperRelease *github.RepositoryRelease
-
-	for _, release := range releases {
-
-		if strings.Contains(*release.TagName, "rc") {
-			continue
-		}
-		firstProperRelease = release
-		break
-	}
-
-	if firstProperRelease == nil {
-		exitWithError(fmt.Errorf("no release found"))
-		return
-	}
-
-	newBranchName := "upgrade-to-rollouts-" + *firstProperRelease.TagName
-
-	// 3) Create, commit, and push a new branch
-	if repoAlreadyUpToDate, err := createNewCommitAndBranch(*firstProperRelease.TagName, "quay.io/argoproj/argo-rollouts", newBranchName, pathToGitHubRepo); err != nil {
-
-		if repoAlreadyUpToDate {
-			fmt.Println("* Exiting as target repository is already up to date.")
-			return
-		}
-
-		exitWithError(err)
-		return
-	}
-
-	if !readOnly {
-
-		bodyText := "Update to latest release of Argo Rollouts"
-
-		if firstProperRelease != nil && firstProperRelease.HTMLURL != nil && *firstProperRelease.HTMLURL != "" {
-			bodyText += ": " + *firstProperRelease.HTMLURL
-		}
-
-		bodyText += `
-Before merging this PR, ensure you check the Argo Rollouts change logs and release notes: 
-- ensure there are no changes to the Argo Rollouts install YAML that we need to respond to with changes in the operator
-- ensure there are no backwards incompatible API/behaviour changes in the change logs`
-
-		// 4) Create PR if it doesn't exist
-		if stdout, stderr, err := runCommandWithWorkDir(pathToGitHubRepo, "gh", "pr", "create",
-			"-R", argoprojlabsRepoOrg+"/"+argoRolloutsManagerRepoName,
-			"--title", PRTitle+(*firstProperRelease.TagName), "--body", bodyText); err != nil {
-			fmt.Println(stdout, stderr)
-			exitWithError(err)
-			return
-		}
-	}
-
-}
-
-// return true if the argo-rollouts-manager repo is already up to date
-func createNewCommitAndBranch(latestReleaseVersionTag string, latestReleaseVersionImage, newBranchName, pathToGitRepo string) (bool, error) {
-
-	commands := [][]string{
-		{"git", "stash"},
-		{"git", "fetch", "parent"},
-		{"git", "checkout", "main"},
-		{"git", "rebase", "parent/main"},
-		{"git", "checkout", "-b", newBranchName},
-	}
-
-	if err := runCommandListWithWorkDir(pathToGitRepo, commands); err != nil {
-		return false, err
-	}
-
-	if repoTargetVersion, err := extractCurrentTargetVersionFromRepo(pathToGitRepo); err != nil {
-		return false, fmt.Errorf("unable to extract current target version from repo")
-	} else if repoTargetVersion == latestReleaseVersionTag {
-		return true, fmt.Errorf("target repository is already on the most recent version")
-	}
-
-	if err := regenerateControllersDefaultGo(latestReleaseVersionTag, latestReleaseVersionImage, pathToGitRepo); err != nil {
-		return false, err
-	}
-
-	if err := regenerateGoMod(latestReleaseVersionTag, pathToGitRepo); err != nil {
-		return false, err
-	}
-
-	if err := regenerateArgoRolloutsE2ETestScriptMod(latestReleaseVersionTag, pathToGitRepo); err != nil {
-		return false, err
-	}
-
-	if err := copyCRDsFromRolloutsRepo(latestReleaseVersionTag, pathToGitRepo); err != nil {
-		return false, fmt.Errorf("unable to copy rollouts CRDs: %w", err)
-	}
-
-	commands = [][]string{
-		{"go", "mod", "tidy"},
-		{"make", "generate", "manifests"},
-		{"make", "bundle"},
-		{"make", "fmt"},
-		{"git", "add", "--all"},
-		{"git", "commit", "-s", "-m", PRTitle + latestReleaseVersionTag},
-	}
-	if err := runCommandListWithWorkDir(pathToGitRepo, commands); err != nil {
-		return false, err
-	}
-
-	if !readOnly {
-		commands = [][]string{
-			{"git", "push", "-f", "--set-upstream", "origin", newBranchName},
-		}
-		if err := runCommandListWithWorkDir(pathToGitRepo, commands); err != nil {
-			return false, err
-		}
-	}
-
-	return false, nil
-
-}
-
-func copyCRDsFromRolloutsRepo(latestReleaseVersionTag string, pathToGitRepo string) error {
-
-	rolloutsRepoPath, err := checkoutRolloutsRepoIntoTempDir(latestReleaseVersionTag)
-	if err != nil {
-		return err
-	}
-
-	crdPath := filepath.Join(rolloutsRepoPath, "manifests/crds")
-	crdYamlDirEntries, err := os.ReadDir(crdPath)
-	if err != nil {
-		return err
-	}
-
-	var crdYAMLs []string
-	for _, crdYamlDirEntry := range crdYamlDirEntries {
-
-		if crdYamlDirEntry.Name() == "kustomization.yaml" {
-			continue
-		}
-
-		if !crdYamlDirEntry.IsDir() {
-			crdYAMLs = append(crdYAMLs, crdYamlDirEntry.Name())
-		}
-	}
-
-	sort.Strings(crdYAMLs)
-
-	// NOTE: If this line fails, check if any new CRDs have been added to Rollouts, and/or if they have changed the filenames.
-	// - If so, this will require verifying the changes, then updating this list
-	if !reflect.DeepEqual(crdYAMLs, []string{
-		"analysis-run-crd.yaml",
-		"analysis-template-crd.yaml",
-		"cluster-analysis-template-crd.yaml",
-		"experiment-crd.yaml",
-		"rollout-crd.yaml"}) {
-		return fmt.Errorf("unexpected CRDs found: %v", crdYAMLs)
-	}
-
-	destinationPath := filepath.Join(pathToGitRepo, "config/crd/bases")
-	for _, crdYAML := range crdYAMLs {
-
-		destFile, err := os.Create(filepath.Join(destinationPath, crdYAML))
-		if err != nil {
-			return fmt.Errorf("unable to create file for '%s': %w", crdYAML, err)
-		}
-		defer destFile.Close()
-
-		srcFile, err := os.Open(filepath.Join(crdPath, crdYAML))
-		if err != nil {
-			return fmt.Errorf("unable to open source file for '%s': %w", crdYAML, err)
-		}
-		defer srcFile.Close()
-
-		_, err = io.Copy(destFile, srcFile)
-		if err != nil {
-			return fmt.Errorf("unable to copy file for '%s': %w", crdYAML, err)
-		}
-
-	}
-
-	return nil
-}
-
-func checkoutRolloutsRepoIntoTempDir(latestReleaseVersionTag string) (string, error) {
-
-	tmpDir, err := os.MkdirTemp("", "argo-rollouts-src")
-	if err != nil {
-		return "", err
-	}
-
-	if _, _, err := runCommandWithWorkDir(tmpDir, "git", "clone", "https://github.com/argoproj/argo-rollouts"); err != nil {
-		return "", err
-	}
-
-	newWorkDir := filepath.Join(tmpDir, "argo-rollouts")
-
-	commands := [][]string{
-		{"git", "checkout", latestReleaseVersionTag},
-	}
-
-	if err := runCommandListWithWorkDir(newWorkDir, commands); err != nil {
-		return "", err
-	}
-
-	return newWorkDir, nil
-}
-
-func runCommandListWithWorkDir(workingDir string, commands [][]string) error {
-
-	for _, command := range commands {
-
-		_, _, err := runCommandWithWorkDir(workingDir, command...)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func regenerateGoMod(latestReleaseVersionTag string, pathToGitRepo string) error {
-
-	// Format of string to modify:
-	//	github.com/argoproj/argo-rollouts v1.6.3
-
-	path := filepath.Join(pathToGitRepo, "go.mod")
-
-	fileBytes, err := os.ReadFile(path)
-	if err != nil {
-		return err
-	}
-
-	var res string
-
-	for _, line := range strings.Split(string(fileBytes), "\n") {
-
-		if strings.Contains(line, "\tgithub.com/argoproj/argo-rollouts v") {
-
-			res += "\tgithub.com/argoproj/argo-rollouts " + latestReleaseVersionTag + "\n"
-
-		} else {
-			res += line + "\n"
-		}
-
-	}
-
-	if err := os.WriteFile(path, []byte(res), 0600); err != nil {
-		return err
-	}
-
-	return nil
-
-}
-
-func regenerateArgoRolloutsE2ETestScriptMod(latestReleaseVersionTag string, pathToGitRepo string) error {
-
-	// Format of string to modify:
-	// CURRENT_ROLLOUTS_VERSION=v1.6.4
-
-	path := filepath.Join(pathToGitRepo, "hack/run-upstream-argo-rollouts-e2e-tests.sh")
-
-	fileBytes, err := os.ReadFile(path)
-	if err != nil {
-		return err
-	}
-
-	var res string
-
-	for _, line := range strings.Split(string(fileBytes), "\n") {
-
-		if strings.Contains(line, "CURRENT_ROLLOUTS_VERSION=") {
-
-			res += "CURRENT_ROLLOUTS_VERSION=" + latestReleaseVersionTag + "\n"
-
-		} else {
-			res += line + "\n"
-		}
-
-	}
-
-	if err := os.WriteFile(path, []byte(res), 0600); err != nil {
-		return err
-	}
-
-	return nil
-
-}
-
-// extractCurrentTargetVersionFromRepo read the contents of the argo-rollouts-manager repo and determine which argo-rollouts version is being targeted.
-func extractCurrentTargetVersionFromRepo(pathToGitRepo string) (string, error) {
-
-	// Style of text string to parse:
-	// DefaultArgoRolloutsVersion = "sha256:995450a0a7f7843d68e96d1a7f63422fa29b245c58f7b57dd0cf9cad72b8308f" //v1.4.1
-
-	path := filepath.Join(pathToGitRepo, controllersDefaultGo)
-
-	fileBytes, err := os.ReadFile(path)
-	if err != nil {
-		return "", err
-	}
-
-	for _, line := range strings.Split(string(fileBytes), "\n") {
-		if strings.Contains(line, "DefaultArgoRolloutsVersion") {
-
-			indexOfForwardSlash := strings.LastIndex(line, "/")
-			if indexOfForwardSlash != -1 {
-				return strings.TrimSpace(line[indexOfForwardSlash+1:]), nil
-			}
-
-		}
-	}
-
-	return "", fmt.Errorf("no version found in '" + controllersDefaultGo + "'")
-}
-
-func regenerateControllersDefaultGo(latestReleaseVersionTag string, latestReleaseVersionImage, pathToGitRepo string) error {
-
-	// Style of text string to replace:
-	// DefaultArgoRolloutsVersion = "sha256:995450a0a7f7843d68e96d1a7f63422fa29b245c58f7b57dd0cf9cad72b8308f" //v1.4.1
-
-	path := filepath.Join(pathToGitRepo, controllersDefaultGo)
-
-	fileBytes, err := os.ReadFile(path)
-	if err != nil {
-		return err
-	}
-
-	var res string
-
-	for _, line := range strings.Split(string(fileBytes), "\n") {
-
-		if strings.Contains(line, "DefaultArgoRolloutsVersion") {
-
-			res += "	DefaultArgoRolloutsVersion = \"" + latestReleaseVersionTag + "\" // " + latestReleaseVersionTag + "\n"
-
-		} else {
-			res += line + "\n"
-		}
-
-	}
-
-	if err := os.WriteFile(path, []byte(res), 0600); err != nil {
-		return err
-	}
-
-	return nil
-
-}
-
-func runCommandWithWorkDir(workingDir string, cmdList ...string) (string, string, error) {
-
-	fmt.Println(cmdList)
-
-	cmd := exec.Command(cmdList[0], cmdList[1:]...)
-	var stdout bytes.Buffer
-	var stderr bytes.Buffer
-	cmd.Dir = workingDir
-	cmd.Stdout = &stdout
-	cmd.Stderr = &stderr
-
-	err := cmd.Run()
-	stdoutStr := stdout.String()
-	stderrStr := stderr.String()
-
-	fmt.Println(stdoutStr, stderrStr)
-
-	return stdoutStr, stderrStr, err
-
-}
-
-func exitWithError(err error) {
-	fmt.Println("ERROR:", err)
-	os.Exit(1)
-}
diff --git a/hack/upgrade-rollouts-script/settings_template.env b/hack/upgrade-rollouts-script/settings_template.env
deleted file mode 100644
index 15aeb2c..0000000
--- a/hack/upgrade-rollouts-script/settings_template.env
+++ /dev/null
@@ -1,2 +0,0 @@
-export GITHUB_FORK_USERNAME="(your username here)"
-export GH_TOKEN="(a GitHub personal access token that can clone/push/open PRs against argo-rollouts-manager repo)"
\ No newline at end of file
diff --git a/main.go b/main.go
index 253305e..c6adec6 100644
--- a/main.go
+++ b/main.go
@@ -49,6 +49,10 @@ func init() {
 	//+kubebuilder:scaffold:scheme
 }
 
+const (
+	DefaultOpenShiftRoutePluginURL = "https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-openshift/releases/download/commit-2749e0ac96ba00ce6f4af19dc6d5358048227d77/rollouts-plugin-trafficrouter-openshift-linux-amd64"
+)
+
 func main() {
 	var metricsAddr string
 	var enableLeaderElection bool
@@ -90,9 +94,16 @@ func main() {
 		os.Exit(1)
 	}
 
+	openShiftRoutePluginLocation := os.Getenv("OPENSHIFT_ROUTE_PLUGIN_LOCATION")
+
+	if openShiftRoutePluginLocation == "" {
+		openShiftRoutePluginLocation = DefaultOpenShiftRoutePluginURL
+	}
+
 	if err = (&controllers.RolloutManagerReconciler{
-		Client: mgr.GetClient(),
-		Scheme: mgr.GetScheme(),
+		Client:                       mgr.GetClient(),
+		Scheme:                       mgr.GetScheme(),
+		OpenShiftRoutePluginLocation: openShiftRoutePluginLocation,
 	}).SetupWithManager(mgr); err != nil {
 		setupLog.Error(err, "unable to create controller", "controller", "RolloutManager")
 		os.Exit(1)


Test patch:
diff --git a/controllers/configmap_test.go b/controllers/configmap_test.go
new file mode 100644
index 0000000..2792330
--- /dev/null
+++ b/controllers/configmap_test.go
@@ -0,0 +1,119 @@
+package rollouts
+
+import (
+	"context"
+
+	"github.com/argoproj-labs/argo-rollouts-manager/api/v1alpha1"
+	"github.com/argoproj/argo-rollouts/utils/plugin/types"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"gopkg.in/yaml.v2"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+var _ = Describe("ConfigMap Test", func() {
+	var ctx context.Context
+	var a *v1alpha1.RolloutManager
+	var r *RolloutManagerReconciler
+
+	BeforeEach(func() {
+		ctx = context.Background()
+		a = makeTestRolloutManager()
+
+		r = makeTestReconciler(a)
+		Expect(createNamespace(r, a.Namespace)).To(Succeed())
+	})
+
+	It("verifies that the default ConfigMap is created if it is not present", func() {
+		expectedConfigMap := &corev1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: DefaultRolloutsConfigMapName,
+			},
+		}
+
+		By("Call reconcileConfigMap")
+		Expect(r.reconcileConfigMap(ctx, a)).To(Succeed())
+
+		By("Verify that the fetched ConfigMap matches the desired one")
+
+		fetchedConfigMap := &corev1.ConfigMap{}
+		Expect(fetchObject(ctx, r.Client, a.Namespace, expectedConfigMap.Name, fetchedConfigMap)).To(Succeed())
+
+		Expect(fetchedConfigMap.Name).To(Equal(expectedConfigMap.Name))
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(OpenShiftRolloutPluginName))
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(r.OpenShiftRoutePluginLocation))
+
+		By("Call reconcileConfigMap again")
+		Expect(r.reconcileConfigMap(ctx, a)).To(Succeed())
+
+		By("verifying that the data is still present")
+		Expect(fetchedConfigMap.Name).To(Equal(expectedConfigMap.Name))
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(OpenShiftRolloutPluginName))
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(r.OpenShiftRoutePluginLocation))
+
+	})
+
+	It("verifies that the config map reconciler will not overwrite a custom plugin that is added to the ConfigMap by the user", func() {
+
+		// By("creating a ConfigMap containing default Openshift")
+		expectedConfigMap := &corev1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      DefaultRolloutsConfigMapName,
+				Namespace: a.Namespace,
+				Labels: map[string]string{
+					"app.kubernetes.io/name": DefaultRolloutsConfigMapName,
+				},
+			},
+		}
+
+		By("calling reconcileConfigMap, which will add the default plugin to the ConfigMap")
+		Expect(r.reconcileConfigMap(ctx, a)).To(Succeed())
+
+		By("fetching the ConfigMap")
+		fetchedConfigMap := &corev1.ConfigMap{}
+		Expect(fetchObject(ctx, r.Client, a.Namespace, expectedConfigMap.Name, fetchedConfigMap)).To(Succeed())
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(OpenShiftRolloutPluginName))
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).ToNot(ContainSubstring("test/plugin"))
+
+		By("adding a new trafficRouter plugin to test the update plugin logic")
+		trafficRouterPlugins := []types.PluginItem{
+			{
+				Name:     "test/plugin",
+				Location: "https://test-path",
+			},
+		}
+
+		newConfigMap := fetchedConfigMap.DeepCopy()
+		{
+			pluginString, err := yaml.Marshal(trafficRouterPlugins)
+			Expect(err).ToNot(HaveOccurred())
+
+			newConfigMap.Data = map[string]string{
+				TrafficRouterPluginConfigMapKey: string(pluginString),
+			}
+		}
+
+		By("updating the ConfigMap to contain only a user provided plugin")
+		Expect(r.Client.Update(ctx, newConfigMap)).To(Succeed())
+
+		By("calling reconcileConfigMap")
+		Expect(r.reconcileConfigMap(ctx, a)).To(Succeed())
+
+		By("verifing that when ConfigMap is reconciled, it contains both plugins")
+
+		Expect(fetchObject(ctx, r.Client, a.Namespace, expectedConfigMap.Name, fetchedConfigMap)).To(Succeed())
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring("test/plugin"))
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(OpenShiftRolloutPluginName))
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(r.OpenShiftRoutePluginLocation))
+
+		By("calling reconcileConfigMap again, to verify nothing changes when reconcile is called again")
+		Expect(r.reconcileConfigMap(ctx, a)).To(Succeed())
+
+		Expect(fetchObject(ctx, r.Client, a.Namespace, expectedConfigMap.Name, fetchedConfigMap)).To(Succeed())
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring("test/plugin"))
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(OpenShiftRolloutPluginName))
+		Expect(fetchedConfigMap.Data[TrafficRouterPluginConfigMapKey]).To(ContainSubstring(r.OpenShiftRoutePluginLocation))
+
+	})
+})
diff --git a/controllers/deployment_test.go b/controllers/deployment_test.go
new file mode 100644
index 0000000..69f8c30
--- /dev/null
+++ b/controllers/deployment_test.go
@@ -0,0 +1,133 @@
+package rollouts
+
+import (
+	"context"
+
+	"github.com/argoproj-labs/argo-rollouts-manager/api/v1alpha1"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+var _ = Describe("Deployment Test", func() {
+	var ctx context.Context
+	var a *v1alpha1.RolloutManager
+	var r *RolloutManagerReconciler
+	var sa *corev1.ServiceAccount
+
+	BeforeEach(func() {
+		ctx = context.Background()
+		a = makeTestRolloutManager()
+
+		r = makeTestReconciler(a)
+		Expect(createNamespace(r, a.Namespace)).To(Succeed())
+
+		sa = &corev1.ServiceAccount{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      DefaultArgoRolloutsResourceName,
+				Namespace: a.Namespace,
+			},
+		}
+		Expect(r.Client.Create(ctx, sa)).To(Succeed())
+	})
+
+	It("should create a new deployment if it does not exist", func() {
+
+		By("calling reconcileRolloutsDeployment")
+		Expect(r.reconcileRolloutsDeployment(ctx, a, sa)).To(Succeed())
+
+		By("fetch the Deployment")
+		fetchedDeployment := &appsv1.Deployment{}
+		Expect(fetchObject(ctx, r.Client, a.Namespace, DefaultArgoRolloutsResourceName, fetchedDeployment)).To(Succeed())
+
+		expectedDeployment := deploymentCR(DefaultArgoRolloutsResourceName, a.Namespace, DefaultArgoRolloutsResourceName, "tmp", "linux", DefaultArgoRolloutsResourceName, a)
+
+		By("verify that the fetched Deployment matches the desired one")
+		Expect(fetchedDeployment.Name).To(Equal(expectedDeployment.Name))
+		Expect(fetchedDeployment.Labels).To(Equal(expectedDeployment.Labels))
+		Expect(fetchedDeployment.Spec.Template.Spec.ServiceAccountName).To(Equal(expectedDeployment.Spec.Template.Spec.ServiceAccountName))
+		Expect(fetchedDeployment.Spec.Template.Labels).To(Equal(expectedDeployment.Spec.Template.Labels))
+		Expect(fetchedDeployment.Spec.Selector).To(Equal(expectedDeployment.Spec.Selector))
+		Expect(fetchedDeployment.Spec.Template.Spec.NodeSelector).To(Equal(expectedDeployment.Spec.Template.Spec.NodeSelector))
+		Expect(fetchedDeployment.Spec.Template.Spec.Tolerations).To(Equal(expectedDeployment.Spec.Template.Spec.Tolerations))
+		Expect(fetchedDeployment.Spec.Template.Spec.SecurityContext).To(Equal(expectedDeployment.Spec.Template.Spec.SecurityContext))
+		Expect(fetchedDeployment.Spec.Template.Spec.Volumes).To(Equal(expectedDeployment.Spec.Template.Spec.Volumes))
+	})
+
+	It("should update the Deployment back to default values, if deployment already exists and has been modified away from default", func() {
+
+		By("create a new Deployment with custom values")
+		existingDeployment := deploymentCR(DefaultArgoRolloutsResourceName, a.Namespace, "test-resource-name", "tmp-test", "linux-test", sa.Name, a)
+
+		Expect(r.Client.Create(ctx, existingDeployment)).To(Succeed())
+
+		By("calling reconcileRolloutsDeployment")
+		Expect(r.reconcileRolloutsDeployment(ctx, a, sa)).To(Succeed())
+
+		By("fetch the Deployment")
+		fetchedDeployment := &appsv1.Deployment{}
+		Expect(fetchObject(ctx, r.Client, a.Namespace, DefaultArgoRolloutsResourceName, fetchedDeployment)).To(Succeed())
+
+		expectedDeployment := deploymentCR(DefaultArgoRolloutsResourceName, a.Namespace, DefaultArgoRolloutsResourceName, "tmp", "linux", sa.Name, a)
+
+		By("verify that the Deployment has been reconciled back to default values")
+		Expect(fetchedDeployment.Name).To(Equal(expectedDeployment.Name))
+		Expect(fetchedDeployment.Labels).To(Equal(expectedDeployment.Labels))
+		Expect(fetchedDeployment.Spec.Template.Spec.ServiceAccountName).To(Equal(expectedDeployment.Spec.Template.Spec.ServiceAccountName))
+		Expect(fetchedDeployment.Spec.Template.Labels).To(Equal(expectedDeployment.Spec.Template.Labels))
+		Expect(fetchedDeployment.Spec.Selector).To(Equal(expectedDeployment.Spec.Selector))
+		Expect(fetchedDeployment.Spec.Template.Spec.NodeSelector).To(Equal(expectedDeployment.Spec.Template.Spec.NodeSelector))
+		Expect(fetchedDeployment.Spec.Template.Spec.Tolerations).To(Equal(expectedDeployment.Spec.Template.Spec.Tolerations))
+		Expect(fetchedDeployment.Spec.Template.Spec.SecurityContext).To(Equal(expectedDeployment.Spec.Template.Spec.SecurityContext))
+		Expect(fetchedDeployment.Spec.Template.Spec.Volumes).To(Equal(expectedDeployment.Spec.Template.Spec.Volumes))
+
+	})
+
+})
+
+func deploymentCR(name string, namespace string, label string, volumeName string, nodeSelector string, serviceAccount string, rolloutManager *v1alpha1.RolloutManager) *appsv1.Deployment {
+	runAsNonRoot := true
+	deploymentCR := &appsv1.Deployment{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+	}
+	setRolloutsLabels(&deploymentCR.ObjectMeta)
+	deploymentCR.Spec = appsv1.DeploymentSpec{
+		Selector: &metav1.LabelSelector{
+			MatchLabels: map[string]string{
+				DefaultRolloutsSelectorKey: label,
+			},
+		},
+		Template: corev1.PodTemplateSpec{
+			ObjectMeta: metav1.ObjectMeta{
+				Labels: map[string]string{
+					DefaultRolloutsSelectorKey: label,
+				},
+			},
+			Spec: corev1.PodSpec{
+				Volumes: []corev1.Volume{
+					{
+						Name: volumeName,
+					},
+				},
+				NodeSelector: map[string]string{
+					"kubernetes.io/os": nodeSelector,
+				},
+				Containers: []corev1.Container{
+					rolloutsContainer(rolloutManager),
+				},
+				ServiceAccountName: serviceAccount,
+				SecurityContext: &corev1.PodSecurityContext{
+					RunAsNonRoot: &runAsNonRoot,
+				},
+			},
+		},
+	}
+
+	return deploymentCR
+
+}
diff --git a/controllers/resources_test.go b/controllers/resources_test.go
index f326527..def5f30 100644
--- a/controllers/resources_test.go
+++ b/controllers/resources_test.go
@@ -26,8 +26,7 @@ var _ = Describe("ReconcileRolloutManager tests", func() {
 		ctx = context.Background()
 		a = makeTestRolloutManager()
 		r = makeTestReconciler(a)
-		err := createNamespace(r, a.Namespace)
-		Expect(err).ToNot(HaveOccurred())
+		Expect(createNamespace(r, a.Namespace)).To(Succeed())
 
 		req = reconcile.Request{
 			NamespacedName: types.NamespacedName{
@@ -89,43 +88,20 @@ var _ = Describe("ReconcileRolloutManager tests", func() {
 	})
 
 	It("ReconcileAggregate to adminClusterRole test", func() {
-		err := r.reconcileRolloutsAggregateToAdminClusterRole(context.Background(), a)
-		Expect(err).ToNot(HaveOccurred())
+		Expect(r.reconcileRolloutsAggregateToAdminClusterRole(context.Background(), a)).To(Succeed())
 	})
 
 	It("ReconcileAggregate to EditClusterRole test", func() {
-		err := r.reconcileRolloutsAggregateToEditClusterRole(context.Background(), a)
-		Expect(err).ToNot(HaveOccurred())
+		Expect(r.reconcileRolloutsAggregateToEditClusterRole(context.Background(), a)).To(Succeed())
 	})
 
 	It("ReconcileAggregate to ViewClusterRole", func() {
-		err := r.reconcileRolloutsAggregateToViewClusterRole(context.Background(), a)
-		Expect(err).ToNot(HaveOccurred())
+		Expect(r.reconcileRolloutsAggregateToViewClusterRole(context.Background(), a)).To(Succeed())
 	})
 
 	Context("RolloutManager Cleaup tests", func() {
-		ctx = context.Background()
 		a = makeTestRolloutManager()
 
-		req = reconcile.Request{
-			NamespacedName: types.NamespacedName{
-				Name:      a.Name,
-				Namespace: a.Namespace,
-			},
-		}
-		resources := []runtime.Object{a}
-
-		r := makeTestReconciler(resources...)
-		err := createNamespace(r, a.Namespace)
-		Expect(err).ToNot(HaveOccurred())
-
-		res, err := r.Reconcile(ctx, req)
-		Expect(err).NotTo(HaveOccurred())
-		Expect(res.Requeue).Should(BeFalse(), "reconcile should not requeue request")
-
-		err = r.Client.Delete(ctx, a)
-		Expect(err).ToNot(HaveOccurred())
-
 		tt := []struct {
 			name     string
 			resource client.Object
@@ -180,9 +156,28 @@ var _ = Describe("ReconcileRolloutManager tests", func() {
 
 		for _, test := range tt {
 			When(test.name, func() {
-				It("ReconcileRolloutManager CleanUp Test for "+test.name+".", func() {
-					err := fetchObject(ctx, r.Client, a.Namespace, test.name, test.resource)
-					Expect(err).To(HaveOccurred(), fmt.Sprintf("Expected %s to be deleted", test.name))
+				It("Cleans up all resources created for RolloutManager", func() {
+
+					ctx := context.Background()
+					req := reconcile.Request{
+						NamespacedName: types.NamespacedName{
+							Name:      a.Name,
+							Namespace: a.Namespace,
+						},
+					}
+					resources := []runtime.Object{a}
+
+					r := makeTestReconciler(resources...)
+					err := createNamespace(r, a.Namespace)
+					Expect(err).ToNot(HaveOccurred())
+
+					res, err := r.Reconcile(ctx, req)
+					Expect(err).NotTo(HaveOccurred())
+					Expect(res.Requeue).Should(BeFalse(), "reconcile should not requeue request")
+
+					err = r.Client.Delete(ctx, a)
+					Expect(err).ToNot(HaveOccurred())
+					Expect(fetchObject(ctx, r.Client, a.Namespace, test.name, test.resource)).ToNot(Succeed(), fmt.Sprintf("Expected %s to be deleted", test.name))
 				})
 			})
 		}
diff --git a/controllers/status_test.go b/controllers/status_test.go
index ca075f7..e600933 100644
--- a/controllers/status_test.go
+++ b/controllers/status_test.go
@@ -16,11 +16,8 @@ var _ = Describe("RolloutManager Test", func() {
 		a := makeTestRolloutManager()
 
 		r := makeTestReconciler(a)
-		err := createNamespace(r, a.Namespace)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = r.reconcileStatus(ctx, a)
-		Expect(err).ToNot(HaveOccurred())
+		Expect(createNamespace(r, a.Namespace)).To(Succeed())
+		Expect(r.reconcileStatus(ctx, a)).To(Succeed())
 
 		By("When deployment for rollout controller does not exist")
 		Expect(a.Status.RolloutController).To(Equal(rolloutsmanagerv1alpha1.PhaseFailure))
@@ -34,11 +31,8 @@ var _ = Describe("RolloutManager Test", func() {
 			},
 		}
 
-		err = r.Client.Create(ctx, deploy)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = r.reconcileStatus(ctx, a)
-		Expect(err).ToNot(HaveOccurred())
+		Expect(r.Client.Create(ctx, deploy)).To(Succeed())
+		Expect(r.reconcileStatus(ctx, a)).To(Succeed())
 
 		Expect(a.Status.RolloutController).To(Equal(rolloutsmanagerv1alpha1.PhaseUnknown))
 		Expect(a.Status.Phase).To(Equal(rolloutsmanagerv1alpha1.PhaseUnknown))
@@ -48,24 +42,19 @@ var _ = Describe("RolloutManager Test", func() {
 		deploy.Status.ReadyReplicas = 0
 		deploy.Spec.Replicas = &requiredReplicas
 
-		err = r.Client.Update(ctx, deploy)
-		Expect(err).ToNot(HaveOccurred())
+		Expect(r.Client.Update(ctx, deploy)).To(Succeed())
 
-		err = r.reconcileStatus(ctx, a)
-		Expect(err).ToNot(HaveOccurred())
+		Expect(r.reconcileStatus(ctx, a)).To(Succeed())
 
 		Expect(a.Status.RolloutController).To(Equal(rolloutsmanagerv1alpha1.PhasePending))
 		Expect(a.Status.Phase).To(Equal(rolloutsmanagerv1alpha1.PhasePending))
 
-		// When deployment exists and required number of replicas are up and running.
+		By("When deployment exists and required number of replicas are up and running.")
 		deploy.Status.ReadyReplicas = 1
 		deploy.Spec.Replicas = &requiredReplicas
 
-		err = r.Client.Update(ctx, deploy)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = r.reconcileStatus(ctx, a)
-		Expect(err).ToNot(HaveOccurred())
+		Expect(r.Client.Update(ctx, deploy)).To(Succeed())
+		Expect(r.reconcileStatus(ctx, a)).To(Succeed())
 
 		Expect(a.Status.RolloutController).To(Equal(rolloutsmanagerv1alpha1.PhaseAvailable))
 		Expect(a.Status.Phase).To(Equal(rolloutsmanagerv1alpha1.PhaseAvailable))
diff --git a/controllers/utils_test.go b/controllers/utils_test.go
index 9139eca..89c574b 100644
--- a/controllers/utils_test.go
+++ b/controllers/utils_test.go
@@ -40,8 +40,9 @@ func makeTestReconciler(objs ...runtime.Object) *RolloutManagerReconciler {
 
 	cl := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...).Build()
 	return &RolloutManagerReconciler{
-		Client: cl,
-		Scheme: s,
+		Client:                       cl,
+		Scheme:                       s,
+		OpenShiftRoutePluginLocation: "file://non-empty-test-url", // Set a non-real, non-empty value for unit tests: override this to test a specific value
 	}
 }