From 4c591575b039ca8dd7ba80e636a4f518091c9a6e Mon Sep 17 00:00:00 2001
From: Ken Sipe <kensipe@gmail.com>
Date: Wed, 5 Aug 2020 14:14:50 -0500
Subject: [PATCH 1/4] GolangCI-Lint Added with Happy Linter

Signed-off-by: Ken Sipe <kensipe@gmail.com>
---
 .golangci.yml                             | 25 +++++++++++++++++++++++
 internal/cmd/catalog_list.go              |  2 +-
 internal/cmd/operator_list.go             |  2 +-
 internal/cmd/operator_list_available.go   |  2 +-
 internal/pkg/action/operator_install.go   | 11 ++++++----
 internal/pkg/action/operator_uninstall.go |  8 ++++----
 internal/pkg/action/operator_upgrade.go   |  4 ++--
 7 files changed, 41 insertions(+), 13 deletions(-)
 create mode 100644 .golangci.yml

diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 00000000..e3bade34
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,25 @@
+linters:
+  auto-fix: false
+  enable:
+    - errcheck
+    - goimports
+    - golint
+    - gosec
+    - misspell
+    - scopelint
+    - unconvert
+    - unparam
+    - interfacer
+    - nakedret
+    - gocyclo
+    - dupl
+    - goconst
+    - lll
+run:
+linters-settings:
+  errcheck:
+    check-type-assertions: true
+  lll:
+    line-length: 250
+  dupl:
+    threshold: 400
diff --git a/internal/cmd/catalog_list.go b/internal/cmd/catalog_list.go
index e47c49b4..ca3de0d2 100644
--- a/internal/cmd/catalog_list.go
+++ b/internal/cmd/catalog_list.go
@@ -49,7 +49,7 @@ func newCatalogListCmd(cfg *action.Configuration) *cobra.Command {
 				if allNamespaces {
 					ns = "\t" + cs.Namespace
 				}
-				age := time.Now().Sub(cs.CreationTimestamp.Time)
+				age := time.Since(cs.CreationTimestamp.Time)
 				_, _ = fmt.Fprintf(tw, "%s%s\t%s\t%s\t%s\t%s\n", cs.Name, ns, cs.Spec.DisplayName, cs.Spec.SourceType, cs.Spec.Publisher, duration.HumanDuration(age))
 			}
 			_ = tw.Flush()
diff --git a/internal/cmd/operator_list.go b/internal/cmd/operator_list.go
index cfc5bc67..65464752 100644
--- a/internal/cmd/operator_list.go
+++ b/internal/cmd/operator_list.go
@@ -55,7 +55,7 @@ func newOperatorListCmd(cfg *action.Configuration) *cobra.Command {
 				if allNamespaces {
 					ns = "\t" + sub.Namespace
 				}
-				age := time.Now().Sub(sub.CreationTimestamp.Time)
+				age := time.Since(sub.CreationTimestamp.Time)
 				_, _ = fmt.Fprintf(tw, "%s%s\t%s\t%s\t%s\t%s\t%s\n", sub.Spec.Package, ns, sub.Name, sub.Status.InstalledCSV, sub.Status.CurrentCSV, sub.Status.State, duration.HumanDuration(age))
 			}
 			_ = tw.Flush()
diff --git a/internal/cmd/operator_list_available.go b/internal/cmd/operator_list_available.go
index 9e6de2d8..eda5ca4a 100644
--- a/internal/cmd/operator_list_available.go
+++ b/internal/cmd/operator_list_available.go
@@ -48,7 +48,7 @@ func newOperatorListAvailableCmd(cfg *action.Configuration) *cobra.Command {
 			tw := tabwriter.NewWriter(os.Stdout, 3, 4, 2, ' ', 0)
 			_, _ = fmt.Fprintf(tw, "NAME\tCATALOG\tCHANNEL\tLATEST CSV\tAGE\n")
 			for _, op := range operators {
-				age := time.Now().Sub(op.CreationTimestamp.Time)
+				age := time.Since(op.CreationTimestamp.Time)
 				for _, ch := range op.Status.Channels {
 					_, _ = fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\n", op.Name, op.Status.CatalogSourceDisplayName, ch.Name, ch.CurrentCSV, duration.HumanDuration(age))
 				}
diff --git a/internal/pkg/action/operator_install.go b/internal/pkg/action/operator_install.go
index 30175464..a901f271 100644
--- a/internal/pkg/action/operator_install.go
+++ b/internal/pkg/action/operator_install.go
@@ -51,7 +51,10 @@ func (i *OperatorInstall) BindFlags(fs *pflag.FlagSet) {
 	fs.BoolVarP(&i.CreateOperatorGroup, "create-operator-group", "C", false, "create operator group if necessary")
 
 	fs.VarP(&i.InstallMode, "install-mode", "i", "install mode")
-	fs.MarkHidden("install-mode")
+	err := fs.MarkHidden("install-mode")
+	if err != nil {
+		log.Print(`requested flag "install-mode" missing`)
+	}
 }
 
 func (i *OperatorInstall) Run(ctx context.Context) (*v1alpha1.ClusterServiceVersion, error) {
@@ -134,9 +137,9 @@ func (i *OperatorInstall) getPackageChannel(pm *operatorsv1.PackageManifest) (*o
 		i.Channel = pm.Status.DefaultChannel
 	}
 	var packageChannel *operatorsv1.PackageChannel
-	for _, ch := range pm.Status.Channels {
+	for idx, ch := range pm.Status.Channels {
 		if ch.Name == i.Channel {
-			packageChannel = &ch
+			packageChannel = &pm.Status.Channels[idx]
 		}
 	}
 	if packageChannel == nil {
@@ -308,7 +311,7 @@ func guessStartingCSV(csvNameExample, desiredVersion string) string {
 
 const (
 	operatorNameRegexp = `[a-z0-9]([-a-z0-9]*[a-z0-9])?`
-	semverRegexp       = `(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?`
+	semverRegexp       = `(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?` //nolint:lll
 )
 
 var csvNameRegexp = regexp.MustCompile(`^(` + operatorNameRegexp + `).(v?)(` + semverRegexp + `)$`)
diff --git a/internal/pkg/action/operator_uninstall.go b/internal/pkg/action/operator_uninstall.go
index 26134f2b..dc58d214 100644
--- a/internal/pkg/action/operator_uninstall.go
+++ b/internal/pkg/action/operator_uninstall.go
@@ -48,9 +48,9 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
 	}
 
 	var sub *v1alpha1.Subscription
-	for _, s := range subs.Items {
+	for i, s := range subs.Items {
 		if u.Package == s.Spec.Package {
-			sub = &s
+			sub = &subs.Items[i]
 			break
 		}
 	}
@@ -84,8 +84,8 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
 			if err := u.config.Client.List(ctx, &ogs, client.InNamespace(u.config.Namespace)); err != nil {
 				return fmt.Errorf("list operatorgroups: %v", err)
 			}
-			for _, og := range ogs.Items {
-				if err := u.config.Client.Delete(ctx, &og); err != nil {
+			for i, og := range ogs.Items {
+				if err := u.config.Client.Delete(ctx, &ogs.Items[i]); err != nil {
 					return fmt.Errorf("delete operatorgroup %q: %v", og.Name, err)
 				}
 				log.Printf("operatorgroup %q deleted", og.Name)
diff --git a/internal/pkg/action/operator_upgrade.go b/internal/pkg/action/operator_upgrade.go
index 24cc8cd3..a281548f 100644
--- a/internal/pkg/action/operator_upgrade.go
+++ b/internal/pkg/action/operator_upgrade.go
@@ -38,9 +38,9 @@ func (u *OperatorUpgrade) Run(ctx context.Context) (*v1alpha1.ClusterServiceVers
 	}
 
 	var sub *v1alpha1.Subscription
-	for _, s := range subs.Items {
+	for i, s := range subs.Items {
 		if u.Package == s.Spec.Package {
-			sub = &s
+			sub = &subs.Items[i]
 			break
 		}
 	}

From 4254f64213f5c681ef08dcb7cd84912483e0a7c8 Mon Sep 17 00:00:00 2001
From: Ken Sipe <kensipe@gmail.com>
Date: Wed, 5 Aug 2020 15:14:01 -0500
Subject: [PATCH 2/4] 2nd Approach for
 using-reference-to-loop-iterator-variable

Signed-off-by: Ken Sipe <kensipe@gmail.com>
---
 internal/pkg/action/operator_install.go   |  5 +++--
 internal/pkg/action/operator_uninstall.go | 10 ++++++----
 internal/pkg/action/operator_upgrade.go   |  5 +++--
 3 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/internal/pkg/action/operator_install.go b/internal/pkg/action/operator_install.go
index a901f271..57e9a95e 100644
--- a/internal/pkg/action/operator_install.go
+++ b/internal/pkg/action/operator_install.go
@@ -137,9 +137,10 @@ func (i *OperatorInstall) getPackageChannel(pm *operatorsv1.PackageManifest) (*o
 		i.Channel = pm.Status.DefaultChannel
 	}
 	var packageChannel *operatorsv1.PackageChannel
-	for idx, ch := range pm.Status.Channels {
+	for _, ch := range pm.Status.Channels {
+		ch := ch
 		if ch.Name == i.Channel {
-			packageChannel = &pm.Status.Channels[idx]
+			packageChannel = &ch
 		}
 	}
 	if packageChannel == nil {
diff --git a/internal/pkg/action/operator_uninstall.go b/internal/pkg/action/operator_uninstall.go
index dc58d214..89c12970 100644
--- a/internal/pkg/action/operator_uninstall.go
+++ b/internal/pkg/action/operator_uninstall.go
@@ -48,9 +48,10 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
 	}
 
 	var sub *v1alpha1.Subscription
-	for i, s := range subs.Items {
+	for _, s := range subs.Items {
+		s := s
 		if u.Package == s.Spec.Package {
-			sub = &subs.Items[i]
+			sub = &s
 			break
 		}
 	}
@@ -84,8 +85,9 @@ func (u *OperatorUninstall) Run(ctx context.Context) error {
 			if err := u.config.Client.List(ctx, &ogs, client.InNamespace(u.config.Namespace)); err != nil {
 				return fmt.Errorf("list operatorgroups: %v", err)
 			}
-			for i, og := range ogs.Items {
-				if err := u.config.Client.Delete(ctx, &ogs.Items[i]); err != nil {
+			for _, og := range ogs.Items {
+				og := og
+				if err := u.config.Client.Delete(ctx, &og); err != nil {
 					return fmt.Errorf("delete operatorgroup %q: %v", og.Name, err)
 				}
 				log.Printf("operatorgroup %q deleted", og.Name)
diff --git a/internal/pkg/action/operator_upgrade.go b/internal/pkg/action/operator_upgrade.go
index a281548f..bd0835f0 100644
--- a/internal/pkg/action/operator_upgrade.go
+++ b/internal/pkg/action/operator_upgrade.go
@@ -38,9 +38,10 @@ func (u *OperatorUpgrade) Run(ctx context.Context) (*v1alpha1.ClusterServiceVers
 	}
 
 	var sub *v1alpha1.Subscription
-	for i, s := range subs.Items {
+	for _, s := range subs.Items {
+		s := s
 		if u.Package == s.Spec.Package {
-			sub = &subs.Items[i]
+			sub = &s
 			break
 		}
 	}

From a3bd3d94eba9579e4c37a5b60edcc4752623035e Mon Sep 17 00:00:00 2001
From: Ken Sipe <kensipe@gmail.com>
Date: Wed, 5 Aug 2020 15:22:18 -0500
Subject: [PATCH 3/4] Prefer to panic

Signed-off-by: Ken Sipe <kensipe@gmail.com>
---
 internal/pkg/action/operator_install.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/internal/pkg/action/operator_install.go b/internal/pkg/action/operator_install.go
index 57e9a95e..e4902df8 100644
--- a/internal/pkg/action/operator_install.go
+++ b/internal/pkg/action/operator_install.go
@@ -53,7 +53,7 @@ func (i *OperatorInstall) BindFlags(fs *pflag.FlagSet) {
 	fs.VarP(&i.InstallMode, "install-mode", "i", "install mode")
 	err := fs.MarkHidden("install-mode")
 	if err != nil {
-		log.Print(`requested flag "install-mode" missing`)
+		panic(`requested flag "install-mode" missing`)
 	}
 }
 

From 780d542639afeeae6c1190f1fe70daa85432523e Mon Sep 17 00:00:00 2001
From: Ken Sipe <kensipe@gmail.com>
Date: Wed, 5 Aug 2020 17:30:00 -0500
Subject: [PATCH 4/4] Adding lint target and job step

Signed-off-by: Ken Sipe <kensipe@gmail.com>
---
 .github/workflows/ci.yml |  3 +++
 Makefile                 | 11 ++++++++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9cf7c761..56ceab9d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,6 +15,9 @@ jobs:
       with:
         go-version: 1.14
 
+    - name: Lint
+      run: make lint
+
     - name: Run GoReleaser
       uses: goreleaser/goreleaser-action@v2
       with:
diff --git a/Makefile b/Makefile
index f9234fef..b01f7076 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: all build
+.PHONY: all build lint
 all: build
 
 build:
@@ -6,3 +6,12 @@ build:
 
 install: build
 	install bin/kubectl-operator $(shell go env GOPATH)/bin
+
+GOLANGCI_LINT_VER = "1.29.0"
+lint:
+#	scripts/golangci-lint-check.sh
+ifneq (${GOLANGCI_LINT_VER}, "$(shell ./bin/golangci-lint --version 2>/dev/null | cut -b 27-32)")
+	@echo "golangci-lint missing or not version '${GOLANGCI_LINT_VER}', downloading..."
+	curl -sSfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${GOLANGCI_LINT_VER}/install.sh" | sh -s -- -b ./bin "v${GOLANGCI_LINT_VER}"
+endif
+	./bin/golangci-lint --timeout 3m run
\ No newline at end of file