From 058dfdfcd634ecb1879a7a1f372274db8a9a7b66 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CAnton?= <a.kaymakchi@dodopizza.com>
Date: Sat, 17 Oct 2020 23:35:09 +0300
Subject: [PATCH] Move flags and utils to internal packages

---
 cmd/gotk/bootstrap.go                         | 37 +++++------
 cmd/gotk/bootstrap_github.go                  |  3 +-
 cmd/gotk/bootstrap_gitlab.go                  |  3 +-
 cmd/gotk/check.go                             |  5 +-
 cmd/gotk/create_alert.go                      |  6 +-
 cmd/gotk/create_alertprovider.go              |  3 +-
 cmd/gotk/create_helmrelease.go                |  9 +--
 cmd/gotk/create_kustomization.go              | 11 ++--
 cmd/gotk/create_receiver.go                   |  5 +-
 cmd/gotk/create_source_bucket.go              |  5 +-
 cmd/gotk/create_source_git.go                 | 13 ++--
 cmd/gotk/create_source_helm.go                |  3 +-
 cmd/gotk/create_tenant.go                     |  3 +-
 cmd/gotk/delete_alert.go                      |  3 +-
 cmd/gotk/delete_alertprovider.go              |  3 +-
 cmd/gotk/delete_helmrelease.go                |  3 +-
 cmd/gotk/delete_kustomization.go              |  3 +-
 cmd/gotk/delete_receiver.go                   |  3 +-
 cmd/gotk/delete_source_bucket.go              |  3 +-
 cmd/gotk/delete_source_git.go                 |  3 +-
 cmd/gotk/delete_source_helm.go                |  3 +-
 cmd/gotk/export_alert.go                      |  3 +-
 cmd/gotk/export_alertprovider.go              |  3 +-
 cmd/gotk/export_helmrelease.go                |  3 +-
 cmd/gotk/export_kustomization.go              |  3 +-
 cmd/gotk/export_receiver.go                   |  3 +-
 cmd/gotk/export_source_bucket.go              |  3 +-
 cmd/gotk/export_source_git.go                 |  3 +-
 cmd/gotk/export_source_helm.go                |  3 +-
 cmd/gotk/get_alert.go                         |  5 +-
 cmd/gotk/get_alertprovider.go                 |  5 +-
 cmd/gotk/get_helmrelease.go                   |  5 +-
 cmd/gotk/get_kustomization.go                 |  5 +-
 cmd/gotk/get_receiver.go                      |  5 +-
 cmd/gotk/get_source_bucket.go                 |  5 +-
 cmd/gotk/get_source_git.go                    |  5 +-
 cmd/gotk/get_source_helm.go                   |  5 +-
 cmd/gotk/install.go                           | 33 ++++------
 cmd/gotk/main.go                              | 12 ++--
 cmd/gotk/reconcile_alert.go                   |  6 +-
 cmd/gotk/reconcile_alertprovider.go           |  6 +-
 cmd/gotk/reconcile_helmrelease.go             |  3 +-
 cmd/gotk/reconcile_kustomization.go           |  3 +-
 cmd/gotk/reconcile_receiver.go                |  6 +-
 cmd/gotk/reconcile_source_bucket.go           |  6 +-
 cmd/gotk/reconcile_source_git.go              |  6 +-
 cmd/gotk/reconcile_source_helm.go             |  6 +-
 cmd/gotk/resume_alert.go                      |  4 +-
 cmd/gotk/resume_helmrelease.go                |  4 +-
 cmd/gotk/resume_kustomization.go              |  4 +-
 cmd/gotk/resume_receiver.go                   |  4 +-
 cmd/gotk/suspend_alert.go                     |  3 +-
 cmd/gotk/suspend_helmrelease.go               |  3 +-
 cmd/gotk/suspend_kustomization.go             |  4 +-
 cmd/gotk/suspend_receiver.go                  |  3 +-
 cmd/gotk/uninstall.go                         |  7 +-
 internal/flags/arch.go                        | 54 +++++++++++++++
 .../flags.go => internal/flags/ecdsa_curve.go | 66 +------------------
 internal/flags/log_level.go                   | 54 +++++++++++++++
 internal/flags/public_key_algorithm.go        | 53 +++++++++++++++
 internal/flags/rsa_key_bits.go                | 55 ++++++++++++++++
 {cmd/gotk => internal/utils}/utils.go         | 30 ++++-----
 62 files changed, 416 insertions(+), 213 deletions(-)
 create mode 100644 internal/flags/arch.go
 rename cmd/gotk/flags.go => internal/flags/ecdsa_curve.go (51%)
 create mode 100644 internal/flags/log_level.go
 create mode 100644 internal/flags/public_key_algorithm.go
 create mode 100644 internal/flags/rsa_key_bits.go
 rename {cmd/gotk => internal/utils}/utils.go (87%)

diff --git a/cmd/gotk/bootstrap.go b/cmd/gotk/bootstrap.go
index 2dce249a..06af444e 100644
--- a/cmd/gotk/bootstrap.go
+++ b/cmd/gotk/bootstrap.go
@@ -38,6 +38,8 @@ import (
 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
 
+	"github.com/fluxcd/toolkit/internal/flags"
+	"github.com/fluxcd/toolkit/internal/utils"
 	"github.com/fluxcd/toolkit/pkg/install"
 )
 
@@ -52,11 +54,11 @@ var (
 	bootstrapComponents         []string
 	bootstrapRegistry           string
 	bootstrapImagePullSecret    string
-	bootstrapArch               string
+	bootstrapArch               flags.Arch = "amd64"
 	bootstrapBranch             string
 	bootstrapWatchAllNamespaces bool
 	bootstrapNetworkPolicy      bool
-	bootstrapLogLevel           string
+	bootstrapLogLevel           flags.LogLevel = "info"
 	bootstrapManifestsPath      string
 	bootstrapRequiredComponents = []string{"source-controller", "kustomize-controller"}
 )
@@ -77,8 +79,7 @@ func init() {
 		"container registry where the toolkit images are published")
 	bootstrapCmd.PersistentFlags().StringVar(&bootstrapImagePullSecret, "image-pull-secret", "",
 		"Kubernetes secret name used for pulling the toolkit images from a private registry")
-	bootstrapCmd.PersistentFlags().StringVar(&bootstrapArch, "arch", "amd64",
-		"arch can be amd64 or arm64")
+	bootstrapCmd.PersistentFlags().Var(&bootstrapArch, "arch", bootstrapArch.Description())
 	bootstrapCmd.PersistentFlags().StringVar(&bootstrapBranch, "branch", bootstrapDefaultBranch,
 		"default branch (for GitHub this must match the default branch setting for the organization)")
 	rootCmd.AddCommand(bootstrapCmd)
@@ -86,22 +87,14 @@ func init() {
 		"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
 	bootstrapCmd.PersistentFlags().BoolVar(&bootstrapNetworkPolicy, "network-policy", true,
 		"deny ingress access to the toolkit controllers from other namespaces using network policies")
-	bootstrapCmd.PersistentFlags().StringVar(&bootstrapLogLevel, "log-level", "info", "set the controllers log level")
+	bootstrapCmd.PersistentFlags().Var(&bootstrapLogLevel, "log-level", bootstrapLogLevel.Description())
 	bootstrapCmd.PersistentFlags().StringVar(&bootstrapManifestsPath, "manifests", "", "path to the manifest directory")
 	bootstrapCmd.PersistentFlags().MarkHidden("manifests")
 }
 
 func bootstrapValidate() error {
-	if !utils.containsItemString(supportedArch, bootstrapArch) {
-		return fmt.Errorf("arch %s is not supported, can be %v", bootstrapArch, supportedArch)
-	}
-
-	if !utils.containsItemString(supportedLogLevels, bootstrapLogLevel) {
-		return fmt.Errorf("log level %s is not supported, can be %v", bootstrapLogLevel, supportedLogLevels)
-	}
-
 	for _, component := range bootstrapRequiredComponents {
-		if !utils.containsItemString(bootstrapComponents, component) {
+		if !utils.ContainsItemString(bootstrapComponents, component) {
 			return fmt.Errorf("component %s is required", component)
 		}
 	}
@@ -124,10 +117,10 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
 		Components:             bootstrapComponents,
 		Registry:               bootstrapRegistry,
 		ImagePullSecret:        bootstrapImagePullSecret,
-		Arch:                   bootstrapArch,
+		Arch:                   bootstrapArch.String(),
 		WatchAllNamespaces:     bootstrapWatchAllNamespaces,
 		NetworkPolicy:          bootstrapNetworkPolicy,
-		LogLevel:               bootstrapLogLevel,
+		LogLevel:               bootstrapLogLevel.String(),
 		NotificationController: defaultNotification,
 		ManifestsFile:          fmt.Sprintf("%s.yaml", namespace),
 		Timeout:                timeout,
@@ -151,13 +144,13 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
 
 func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
 	kubectlArgs := []string{"apply", "-f", manifestPath}
-	if _, err := utils.execKubectlCommand(ctx, ModeOS, kubectlArgs...); err != nil {
+	if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...); err != nil {
 		return fmt.Errorf("install failed")
 	}
 
 	for _, deployment := range components {
 		kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
-		if _, err := utils.execKubectlCommand(ctx, ModeOS, kubectlArgs...); err != nil {
+		if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...); err != nil {
 			return fmt.Errorf("install failed")
 		}
 	}
@@ -194,7 +187,7 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
 		return err
 	}
 
-	if err := utils.writeFile(string(gitData), filepath.Join(tmpDir, targetPath, namespace, bootstrapSourceManifest)); err != nil {
+	if err := utils.WriteFile(string(gitData), filepath.Join(tmpDir, targetPath, namespace, bootstrapSourceManifest)); err != nil {
 		return err
 	}
 
@@ -227,11 +220,11 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
 		return err
 	}
 
-	if err := utils.writeFile(string(ksData), filepath.Join(tmpDir, targetPath, namespace, bootstrapKustomizationManifest)); err != nil {
+	if err := utils.WriteFile(string(ksData), filepath.Join(tmpDir, targetPath, namespace, bootstrapKustomizationManifest)); err != nil {
 		return err
 	}
 
-	if err := utils.generateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil {
+	if err := utils.GenerateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil {
 		return err
 	}
 
@@ -240,7 +233,7 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri
 
 func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, targetPath, tmpDir string) error {
 	kubectlArgs := []string{"apply", "-k", filepath.Join(tmpDir, targetPath, namespace)}
-	if _, err := utils.execKubectlCommand(ctx, ModeStderrOS, kubectlArgs...); err != nil {
+	if _, err := utils.ExecKubectlCommand(ctx, utils.ModeStderrOS, kubectlArgs...); err != nil {
 		return err
 	}
 
diff --git a/cmd/gotk/bootstrap_github.go b/cmd/gotk/bootstrap_github.go
index eca646ea..c111269e 100644
--- a/cmd/gotk/bootstrap_github.go
+++ b/cmd/gotk/bootstrap_github.go
@@ -28,6 +28,7 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/fluxcd/pkg/git"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var bootstrapGitHubCmd = &cobra.Command{
@@ -183,7 +184,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
 		logger.Successf("components are up to date")
 	}
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/bootstrap_gitlab.go b/cmd/gotk/bootstrap_gitlab.go
index 3426b7f4..35893f47 100644
--- a/cmd/gotk/bootstrap_gitlab.go
+++ b/cmd/gotk/bootstrap_gitlab.go
@@ -30,6 +30,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	"github.com/fluxcd/pkg/git"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var bootstrapGitLabCmd = &cobra.Command{
@@ -112,7 +113,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
 		IsPersonal: glPersonal,
 	}
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/check.go b/cmd/gotk/check.go
index 64b86b5c..9ec51398 100644
--- a/cmd/gotk/check.go
+++ b/cmd/gotk/check.go
@@ -24,6 +24,7 @@ import (
 	"strings"
 
 	"github.com/blang/semver/v4"
+	"github.com/fluxcd/toolkit/internal/utils"
 	"github.com/spf13/cobra"
 	apimachineryversion "k8s.io/apimachinery/pkg/version"
 	"k8s.io/client-go/kubernetes"
@@ -103,7 +104,7 @@ func kubectlCheck(ctx context.Context, version string) bool {
 	}
 
 	kubectlArgs := []string{"version", "--client", "--output", "json"}
-	output, err := utils.execKubectlCommand(ctx, ModeCapture, kubectlArgs...)
+	output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubectlArgs...)
 	if err != nil {
 		logger.Failuref("kubectl version can't be determined")
 		return false
@@ -173,7 +174,7 @@ func componentsCheck() bool {
 	ok := true
 	for _, deployment := range checkComponents {
 		kubectlArgs := []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
-		if output, err := utils.execKubectlCommand(ctx, ModeCapture, kubectlArgs...); err != nil {
+		if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, kubectlArgs...); err != nil {
 			logger.Failuref("%s: %s", deployment, strings.TrimSuffix(output, "\n"))
 			ok = false
 		} else {
diff --git a/cmd/gotk/create_alert.go b/cmd/gotk/create_alert.go
index 9a943b21..cae14bc7 100644
--- a/cmd/gotk/create_alert.go
+++ b/cmd/gotk/create_alert.go
@@ -19,7 +19,9 @@ package main
 import (
 	"context"
 	"fmt"
+
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	"github.com/spf13/cobra"
 	corev1 "k8s.io/api/core/v1"
@@ -71,7 +73,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
 
 	eventSources := []notificationv1.CrossNamespaceObjectReference{}
 	for _, eventSource := range aEventSources {
-		kind, name := utils.parseObjectKindName(eventSource)
+		kind, name := utils.ParseObjectKindName(eventSource)
 		if kind == "" {
 			return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", eventSource)
 		}
@@ -118,7 +120,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/create_alertprovider.go b/cmd/gotk/create_alertprovider.go
index 9c28782e..f1ab8c6a 100644
--- a/cmd/gotk/create_alertprovider.go
+++ b/cmd/gotk/create_alertprovider.go
@@ -30,6 +30,7 @@ import (
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var createAlertProviderCmd = &cobra.Command{
@@ -115,7 +116,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/create_helmrelease.go b/cmd/gotk/create_helmrelease.go
index 8d77ad31..9776f751 100644
--- a/cmd/gotk/create_helmrelease.go
+++ b/cmd/gotk/create_helmrelease.go
@@ -22,6 +22,7 @@ import (
 	"io/ioutil"
 
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	"github.com/spf13/cobra"
 	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -117,11 +118,11 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
 	if hrSource == "" {
 		return fmt.Errorf("source is required")
 	}
-	sourceKind, sourceName := utils.parseObjectKindName(hrSource)
+	sourceKind, sourceName := utils.ParseObjectKindName(hrSource)
 	if sourceKind == "" {
 		return fmt.Errorf("invalid source '%s', must be in format <kind>/<name>", hrSource)
 	}
-	if !utils.containsItemString(supportedHelmChartSourceKinds, sourceKind) {
+	if !utils.ContainsItemString(supportedHelmChartSourceKinds, sourceKind) {
 		return fmt.Errorf("source kind %s is not supported, can be %v",
 			sourceKind, supportedHelmChartSourceKinds)
 	}
@@ -146,7 +147,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
 		},
 		Spec: helmv2.HelmReleaseSpec{
 			ReleaseName: hrName,
-			DependsOn:   utils.makeDependsOn(hrDependsOn),
+			DependsOn:   utils.MakeDependsOn(hrDependsOn),
 			Interval: metav1.Duration{
 				Duration: interval,
 			},
@@ -186,7 +187,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/create_kustomization.go b/cmd/gotk/create_kustomization.go
index d3fdd0c6..26f6315d 100644
--- a/cmd/gotk/create_kustomization.go
+++ b/cmd/gotk/create_kustomization.go
@@ -34,6 +34,7 @@ import (
 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
 	"github.com/fluxcd/pkg/apis/meta"
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var createKsCmd = &cobra.Command{
@@ -110,11 +111,11 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
 		return fmt.Errorf("source is required")
 	}
 
-	sourceKind, sourceName := utils.parseObjectKindName(ksSource)
+	sourceKind, sourceName := utils.ParseObjectKindName(ksSource)
 	if sourceKind == "" {
 		sourceKind = sourcev1.GitRepositoryKind
 	}
-	if !utils.containsItemString(supportedKustomizationSourceKinds, sourceKind) {
+	if !utils.ContainsItemString(supportedKustomizationSourceKinds, sourceKind) {
 		return fmt.Errorf("source kind %s is not supported, can be %v",
 			sourceKind, supportedKustomizationSourceKinds)
 	}
@@ -142,7 +143,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
 			Labels:    ksLabels,
 		},
 		Spec: kustomizev1.KustomizationSpec{
-			DependsOn: utils.makeDependsOn(ksDependsOn),
+			DependsOn: utils.MakeDependsOn(ksDependsOn),
 			Interval: metav1.Duration{
 				Duration: interval,
 			},
@@ -206,7 +207,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
 	}
 
 	if ksDecryptionProvider != "" {
-		if !utils.containsItemString(supportedDecryptionProviders, ksDecryptionProvider) {
+		if !utils.ContainsItemString(supportedDecryptionProviders, ksDecryptionProvider) {
 			return fmt.Errorf("decryption provider %s is not supported, can be %v",
 				ksDecryptionProvider, supportedDecryptionProviders)
 		}
@@ -227,7 +228,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/create_receiver.go b/cmd/gotk/create_receiver.go
index 08595bfe..697860ea 100644
--- a/cmd/gotk/create_receiver.go
+++ b/cmd/gotk/create_receiver.go
@@ -30,6 +30,7 @@ import (
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var createReceiverCmd = &cobra.Command{
@@ -79,7 +80,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
 
 	resources := []notificationv1.CrossNamespaceObjectReference{}
 	for _, resource := range rcvResources {
-		kind, name := utils.parseObjectKindName(resource)
+		kind, name := utils.ParseObjectKindName(resource)
 		if kind == "" {
 			return fmt.Errorf("invalid event source '%s', must be in format <kind>/<name>", resource)
 		}
@@ -127,7 +128,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/create_source_bucket.go b/cmd/gotk/create_source_bucket.go
index 32a06ea1..7a467e87 100644
--- a/cmd/gotk/create_source_bucket.go
+++ b/cmd/gotk/create_source_bucket.go
@@ -31,6 +31,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var createSourceBucketCmd = &cobra.Command{
@@ -88,7 +89,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
 	name := args[0]
 	secretName := fmt.Sprintf("bucket-%s", name)
 
-	if !utils.containsItemString(supportedSourceBucketProviders, sourceBucketProvider) {
+	if !utils.ContainsItemString(supportedSourceBucketProviders, sourceBucketProvider) {
 		return fmt.Errorf("Bucket provider %s is not supported, can be %v",
 			sourceBucketProvider, supportedSourceBucketProviders)
 	}
@@ -137,7 +138,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/create_source_git.go b/cmd/gotk/create_source_git.go
index ad784841..5330b5ad 100644
--- a/cmd/gotk/create_source_git.go
+++ b/cmd/gotk/create_source_git.go
@@ -20,12 +20,15 @@ import (
 	"context"
 	"crypto/elliptic"
 	"fmt"
-	"github.com/fluxcd/pkg/apis/meta"
 	"io/ioutil"
 	"net/url"
 	"os"
 	"time"
 
+	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/flags"
+	"github.com/fluxcd/toolkit/internal/utils"
+
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
 	"github.com/manifoldco/promptui"
 	"github.com/spf13/cobra"
@@ -90,9 +93,9 @@ var (
 	sourceGitSemver       string
 	sourceGitUsername     string
 	sourceGitPassword     string
-	sourceGitKeyAlgorithm PublicKeyAlgorithm = "rsa"
-	sourceGitRSABits      RSAKeyBits         = 2048
-	sourceGitECDSACurve                      = ECDSACurve{elliptic.P384()}
+	sourceGitKeyAlgorithm flags.PublicKeyAlgorithm = "rsa"
+	sourceGitRSABits      flags.RSAKeyBits         = 2048
+	sourceGitECDSACurve                            = flags.ECDSACurve{Curve: elliptic.P384()}
 )
 
 func init() {
@@ -165,7 +168,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/create_source_helm.go b/cmd/gotk/create_source_helm.go
index bc6c9a08..e8e61f41 100644
--- a/cmd/gotk/create_source_helm.go
+++ b/cmd/gotk/create_source_helm.go
@@ -32,6 +32,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var createSourceHelmCmd = &cobra.Command{
@@ -128,7 +129,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/create_tenant.go b/cmd/gotk/create_tenant.go
index 8119c7fa..8abf61e4 100644
--- a/cmd/gotk/create_tenant.go
+++ b/cmd/gotk/create_tenant.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"fmt"
 
+	"github.com/fluxcd/toolkit/internal/utils"
 	"github.com/spf13/cobra"
 	corev1 "k8s.io/api/core/v1"
 	rbacv1 "k8s.io/api/rbac/v1"
@@ -144,7 +145,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/delete_alert.go b/cmd/gotk/delete_alert.go
index b918f2b7..242969d5 100644
--- a/cmd/gotk/delete_alert.go
+++ b/cmd/gotk/delete_alert.go
@@ -25,6 +25,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var deleteAlertCmd = &cobra.Command{
@@ -50,7 +51,7 @@ func deleteAlertCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/delete_alertprovider.go b/cmd/gotk/delete_alertprovider.go
index 1b641529..9cd7e153 100644
--- a/cmd/gotk/delete_alertprovider.go
+++ b/cmd/gotk/delete_alertprovider.go
@@ -25,6 +25,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var deleteAlertProviderCmd = &cobra.Command{
@@ -50,7 +51,7 @@ func deleteAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/delete_helmrelease.go b/cmd/gotk/delete_helmrelease.go
index 86ec663c..efc7fe87 100644
--- a/cmd/gotk/delete_helmrelease.go
+++ b/cmd/gotk/delete_helmrelease.go
@@ -25,6 +25,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 
 	helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var deleteHelmReleaseCmd = &cobra.Command{
@@ -51,7 +52,7 @@ func deleteHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/delete_kustomization.go b/cmd/gotk/delete_kustomization.go
index 743827a2..07b0acaa 100644
--- a/cmd/gotk/delete_kustomization.go
+++ b/cmd/gotk/delete_kustomization.go
@@ -21,6 +21,7 @@ import (
 	"fmt"
 
 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 	"github.com/manifoldco/promptui"
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
@@ -50,7 +51,7 @@ func deleteKsCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/delete_receiver.go b/cmd/gotk/delete_receiver.go
index 253308e0..d4c0bd1d 100644
--- a/cmd/gotk/delete_receiver.go
+++ b/cmd/gotk/delete_receiver.go
@@ -25,6 +25,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var deleteReceiverCmd = &cobra.Command{
@@ -50,7 +51,7 @@ func deleteReceiverCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/delete_source_bucket.go b/cmd/gotk/delete_source_bucket.go
index 7614e483..a2c2fd29 100644
--- a/cmd/gotk/delete_source_bucket.go
+++ b/cmd/gotk/delete_source_bucket.go
@@ -21,6 +21,7 @@ import (
 	"fmt"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 	"github.com/manifoldco/promptui"
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
@@ -49,7 +50,7 @@ func deleteSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/delete_source_git.go b/cmd/gotk/delete_source_git.go
index 73000fbf..7d393bb9 100644
--- a/cmd/gotk/delete_source_git.go
+++ b/cmd/gotk/delete_source_git.go
@@ -21,6 +21,7 @@ import (
 	"fmt"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 	"github.com/manifoldco/promptui"
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
@@ -49,7 +50,7 @@ func deleteSourceGitCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/delete_source_helm.go b/cmd/gotk/delete_source_helm.go
index 5a9427ca..05887696 100644
--- a/cmd/gotk/delete_source_helm.go
+++ b/cmd/gotk/delete_source_helm.go
@@ -21,6 +21,7 @@ import (
 	"fmt"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 	"github.com/manifoldco/promptui"
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
@@ -49,7 +50,7 @@ func deleteSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/export_alert.go b/cmd/gotk/export_alert.go
index e5a69f07..c837473f 100644
--- a/cmd/gotk/export_alert.go
+++ b/cmd/gotk/export_alert.go
@@ -27,6 +27,7 @@ import (
 	"sigs.k8s.io/yaml"
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var exportAlertCmd = &cobra.Command{
@@ -54,7 +55,7 @@ func exportAlertCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/export_alertprovider.go b/cmd/gotk/export_alertprovider.go
index 88a61182..3d212d63 100644
--- a/cmd/gotk/export_alertprovider.go
+++ b/cmd/gotk/export_alertprovider.go
@@ -27,6 +27,7 @@ import (
 	"sigs.k8s.io/yaml"
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var exportAlertProviderCmd = &cobra.Command{
@@ -54,7 +55,7 @@ func exportAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/export_helmrelease.go b/cmd/gotk/export_helmrelease.go
index 8911c52e..54bce9e3 100644
--- a/cmd/gotk/export_helmrelease.go
+++ b/cmd/gotk/export_helmrelease.go
@@ -27,6 +27,7 @@ import (
 	"sigs.k8s.io/yaml"
 
 	helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var exportHelmReleaseCmd = &cobra.Command{
@@ -55,7 +56,7 @@ func exportHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/export_kustomization.go b/cmd/gotk/export_kustomization.go
index b47a7076..a059e3a9 100644
--- a/cmd/gotk/export_kustomization.go
+++ b/cmd/gotk/export_kustomization.go
@@ -27,6 +27,7 @@ import (
 	"sigs.k8s.io/yaml"
 
 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var exportKsCmd = &cobra.Command{
@@ -55,7 +56,7 @@ func exportKsCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/export_receiver.go b/cmd/gotk/export_receiver.go
index 1acfb164..e8e35e01 100644
--- a/cmd/gotk/export_receiver.go
+++ b/cmd/gotk/export_receiver.go
@@ -27,6 +27,7 @@ import (
 	"sigs.k8s.io/yaml"
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var exportReceiverCmd = &cobra.Command{
@@ -54,7 +55,7 @@ func exportReceiverCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/export_source_bucket.go b/cmd/gotk/export_source_bucket.go
index af9f78d0..3350cdb7 100644
--- a/cmd/gotk/export_source_bucket.go
+++ b/cmd/gotk/export_source_bucket.go
@@ -28,6 +28,7 @@ import (
 	"sigs.k8s.io/yaml"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var exportSourceBucketCmd = &cobra.Command{
@@ -55,7 +56,7 @@ func exportSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/export_source_git.go b/cmd/gotk/export_source_git.go
index 1aeff810..2cf023a6 100644
--- a/cmd/gotk/export_source_git.go
+++ b/cmd/gotk/export_source_git.go
@@ -28,6 +28,7 @@ import (
 	"sigs.k8s.io/yaml"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var exportSourceGitCmd = &cobra.Command{
@@ -55,7 +56,7 @@ func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/export_source_helm.go b/cmd/gotk/export_source_helm.go
index 53df16e4..1b313942 100644
--- a/cmd/gotk/export_source_helm.go
+++ b/cmd/gotk/export_source_helm.go
@@ -28,6 +28,7 @@ import (
 	"sigs.k8s.io/yaml"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var exportSourceHelmCmd = &cobra.Command{
@@ -55,7 +56,7 @@ func exportSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/get_alert.go b/cmd/gotk/get_alert.go
index c30b3226..20c188de 100644
--- a/cmd/gotk/get_alert.go
+++ b/cmd/gotk/get_alert.go
@@ -28,6 +28,7 @@ import (
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var getAlertCmd = &cobra.Command{
@@ -48,7 +49,7 @@ func getAlertCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
@@ -97,6 +98,6 @@ func getAlertCmdRun(cmd *cobra.Command, args []string) error {
 		}
 		rows = append(rows, row)
 	}
-	utils.printTable(os.Stdout, header, rows)
+	utils.PrintTable(os.Stdout, header, rows)
 	return nil
 }
diff --git a/cmd/gotk/get_alertprovider.go b/cmd/gotk/get_alertprovider.go
index a9421d81..6871e445 100644
--- a/cmd/gotk/get_alertprovider.go
+++ b/cmd/gotk/get_alertprovider.go
@@ -26,6 +26,7 @@ import (
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var getAlertProviderCmd = &cobra.Command{
@@ -46,7 +47,7 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
@@ -91,6 +92,6 @@ func getAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
 		}
 		rows = append(rows, row)
 	}
-	utils.printTable(os.Stdout, header, rows)
+	utils.PrintTable(os.Stdout, header, rows)
 	return nil
 }
diff --git a/cmd/gotk/get_helmrelease.go b/cmd/gotk/get_helmrelease.go
index d810e598..b838f57b 100644
--- a/cmd/gotk/get_helmrelease.go
+++ b/cmd/gotk/get_helmrelease.go
@@ -23,6 +23,7 @@ import (
 	"strings"
 
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	"github.com/spf13/cobra"
 	corev1 "k8s.io/api/core/v1"
@@ -50,7 +51,7 @@ func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
@@ -99,6 +100,6 @@ func getHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
 		}
 		rows = append(rows, row)
 	}
-	utils.printTable(os.Stdout, header, rows)
+	utils.PrintTable(os.Stdout, header, rows)
 	return nil
 }
diff --git a/cmd/gotk/get_kustomization.go b/cmd/gotk/get_kustomization.go
index ac8374cd..9bee84c1 100644
--- a/cmd/gotk/get_kustomization.go
+++ b/cmd/gotk/get_kustomization.go
@@ -23,6 +23,7 @@ import (
 	"strings"
 
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
 	"github.com/spf13/cobra"
@@ -49,7 +50,7 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
@@ -98,6 +99,6 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
 		}
 		rows = append(rows, row)
 	}
-	utils.printTable(os.Stdout, header, rows)
+	utils.PrintTable(os.Stdout, header, rows)
 	return nil
 }
diff --git a/cmd/gotk/get_receiver.go b/cmd/gotk/get_receiver.go
index c41bdce0..8f53d96c 100644
--- a/cmd/gotk/get_receiver.go
+++ b/cmd/gotk/get_receiver.go
@@ -28,6 +28,7 @@ import (
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var getReceiverCmd = &cobra.Command{
@@ -48,7 +49,7 @@ func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
@@ -92,6 +93,6 @@ func getReceiverCmdRun(cmd *cobra.Command, args []string) error {
 		}
 		rows = append(rows, row)
 	}
-	utils.printTable(os.Stdout, header, rows)
+	utils.PrintTable(os.Stdout, header, rows)
 	return nil
 }
diff --git a/cmd/gotk/get_source_bucket.go b/cmd/gotk/get_source_bucket.go
index e4323285..76eb7dde 100644
--- a/cmd/gotk/get_source_bucket.go
+++ b/cmd/gotk/get_source_bucket.go
@@ -21,6 +21,7 @@ import (
 	"os"
 
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
 	"github.com/spf13/cobra"
@@ -46,7 +47,7 @@ func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
@@ -97,6 +98,6 @@ func getSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
 		}
 		rows = append(rows, row)
 	}
-	utils.printTable(os.Stdout, header, rows)
+	utils.PrintTable(os.Stdout, header, rows)
 	return nil
 }
diff --git a/cmd/gotk/get_source_git.go b/cmd/gotk/get_source_git.go
index 4afe14fb..f32a7c62 100644
--- a/cmd/gotk/get_source_git.go
+++ b/cmd/gotk/get_source_git.go
@@ -21,6 +21,7 @@ import (
 	"os"
 
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
 	"github.com/spf13/cobra"
@@ -46,7 +47,7 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
@@ -97,6 +98,6 @@ func getSourceGitCmdRun(cmd *cobra.Command, args []string) error {
 		}
 		rows = append(rows, row)
 	}
-	utils.printTable(os.Stdout, header, rows)
+	utils.PrintTable(os.Stdout, header, rows)
 	return nil
 }
diff --git a/cmd/gotk/get_source_helm.go b/cmd/gotk/get_source_helm.go
index 10e3b2b1..b3c67fd4 100644
--- a/cmd/gotk/get_source_helm.go
+++ b/cmd/gotk/get_source_helm.go
@@ -21,6 +21,7 @@ import (
 	"os"
 
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
 	"github.com/spf13/cobra"
@@ -46,7 +47,7 @@ func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
@@ -97,6 +98,6 @@ func getSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
 		}
 		rows = append(rows, row)
 	}
-	utils.printTable(os.Stdout, header, rows)
+	utils.PrintTable(os.Stdout, header, rows)
 	return nil
 }
diff --git a/cmd/gotk/install.go b/cmd/gotk/install.go
index 72a87be5..fdd6426f 100644
--- a/cmd/gotk/install.go
+++ b/cmd/gotk/install.go
@@ -26,6 +26,8 @@ import (
 
 	"github.com/spf13/cobra"
 
+	"github.com/fluxcd/toolkit/internal/flags"
+	"github.com/fluxcd/toolkit/internal/utils"
 	"github.com/fluxcd/toolkit/pkg/install"
 )
 
@@ -57,10 +59,10 @@ var (
 	installComponents         []string
 	installRegistry           string
 	installImagePullSecret    string
-	installArch               string
+	installArch               flags.Arch = "amd64"
 	installWatchAllNamespaces bool
 	installNetworkPolicy      bool
-	installLogLevel           string
+	installLogLevel           flags.LogLevel = "info"
 )
 
 func init() {
@@ -78,25 +80,16 @@ func init() {
 		"container registry where the toolkit images are published")
 	installCmd.Flags().StringVar(&installImagePullSecret, "image-pull-secret", "",
 		"Kubernetes secret name used for pulling the toolkit images from a private registry")
-	installCmd.Flags().StringVar(&installArch, "arch", "amd64",
-		"arch can be amd64 or arm64")
+	installCmd.Flags().Var(&installArch, "arch", installArch.Description())
 	installCmd.Flags().BoolVar(&installWatchAllNamespaces, "watch-all-namespaces", true,
 		"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
-	installCmd.Flags().StringVar(&installLogLevel, "log-level", "info", "set the controllers log level")
+	installCmd.Flags().Var(&installLogLevel, "log-level", installLogLevel.Description())
 	installCmd.Flags().BoolVar(&installNetworkPolicy, "network-policy", true,
 		"deny ingress access to the toolkit controllers from other namespaces using network policies")
 	rootCmd.AddCommand(installCmd)
 }
 
 func installCmdRun(cmd *cobra.Command, args []string) error {
-	if !utils.containsItemString(supportedArch, installArch) {
-		return fmt.Errorf("arch %s is not supported, can be %v", installArch, supportedArch)
-	}
-
-	if !utils.containsItemString(supportedLogLevels, installLogLevel) {
-		return fmt.Errorf("log level %s is not supported, can be %v", bootstrapLogLevel, installLogLevel)
-	}
-
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
@@ -117,10 +110,10 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
 		Components:             installComponents,
 		Registry:               installRegistry,
 		ImagePullSecret:        installImagePullSecret,
-		Arch:                   installArch,
+		Arch:                   installArch.String(),
 		WatchAllNamespaces:     installWatchAllNamespaces,
 		NetworkPolicy:          installNetworkPolicy,
-		LogLevel:               installLogLevel,
+		LogLevel:               installLogLevel.String(),
 		NotificationController: defaultNotification,
 		ManifestsFile:          fmt.Sprintf("%s.yaml", namespace),
 		Timeout:                timeout,
@@ -154,17 +147,17 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
 
 	logger.Successf("manifests build completed")
 	logger.Actionf("installing components in %s namespace", namespace)
-	applyOutput := ModeStderrOS
+	applyOutput := utils.ModeStderrOS
 	if verbose {
-		applyOutput = ModeOS
+		applyOutput = utils.ModeOS
 	}
 
 	kubectlArgs := []string{"apply", "-f", manifest}
 	if installDryRun {
 		args = append(args, "--dry-run=client")
-		applyOutput = ModeOS
+		applyOutput = utils.ModeOS
 	}
-	if _, err := utils.execKubectlCommand(ctx, applyOutput, kubectlArgs...); err != nil {
+	if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubectlArgs...); err != nil {
 		return fmt.Errorf("install failed")
 	}
 
@@ -178,7 +171,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
 	logger.Waitingf("verifying installation")
 	for _, deployment := range installComponents {
 		kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()}
-		if _, err := utils.execKubectlCommand(ctx, applyOutput, kubectlArgs...); err != nil {
+		if _, err := utils.ExecKubectlCommand(ctx, applyOutput, kubectlArgs...); err != nil {
 			return fmt.Errorf("install failed")
 		} else {
 			logger.Successf("%s ready", deployment)
diff --git a/cmd/gotk/main.go b/cmd/gotk/main.go
index 491c31fb..0b305fc9 100644
--- a/cmd/gotk/main.go
+++ b/cmd/gotk/main.go
@@ -100,18 +100,16 @@ var (
 	namespace    string
 	timeout      time.Duration
 	verbose      bool
-	utils        Utils
 	pollInterval                = 2 * time.Second
 	logger       gotklog.Logger = printLogger{}
 )
 
 var (
-	defaultComponents                 = []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}
-	defaultVersion                    = "latest"
-	defaultNamespace                  = "gotk-system"
-	defaultNotification               = "notification-controller"
-	supportedLogLevels                = []string{"debug", "info", "error"}
-	supportedArch                     = []string{"amd64", "arm", "arm64"}
+	defaultComponents   = []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}
+	defaultVersion      = "latest"
+	defaultNamespace    = "gotk-system"
+	defaultNotification = "notification-controller"
+
 	supportedDecryptionProviders      = []string{"sops"}
 	supportedKustomizationSourceKinds = []string{sourcev1.GitRepositoryKind, sourcev1.BucketKind}
 	supportedHelmChartSourceKinds     = []string{sourcev1.HelmRepositoryKind, sourcev1.GitRepositoryKind, sourcev1.BucketKind}
diff --git a/cmd/gotk/reconcile_alert.go b/cmd/gotk/reconcile_alert.go
index fc6e56db..4b5b75d6 100644
--- a/cmd/gotk/reconcile_alert.go
+++ b/cmd/gotk/reconcile_alert.go
@@ -19,9 +19,11 @@ package main
 import (
 	"context"
 	"fmt"
-	"github.com/fluxcd/pkg/apis/meta"
 	"time"
 
+	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
+
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/wait"
@@ -52,7 +54,7 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/reconcile_alertprovider.go b/cmd/gotk/reconcile_alertprovider.go
index c8f4e9ef..b85a374f 100644
--- a/cmd/gotk/reconcile_alertprovider.go
+++ b/cmd/gotk/reconcile_alertprovider.go
@@ -19,9 +19,11 @@ package main
 import (
 	"context"
 	"fmt"
-	"github.com/fluxcd/pkg/apis/meta"
 	"time"
 
+	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
+
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/wait"
@@ -52,7 +54,7 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/reconcile_helmrelease.go b/cmd/gotk/reconcile_helmrelease.go
index af4273ce..b4a8cb5f 100644
--- a/cmd/gotk/reconcile_helmrelease.go
+++ b/cmd/gotk/reconcile_helmrelease.go
@@ -29,6 +29,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
@@ -68,7 +69,7 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/reconcile_kustomization.go b/cmd/gotk/reconcile_kustomization.go
index ff4e5586..81e4e9d1 100644
--- a/cmd/gotk/reconcile_kustomization.go
+++ b/cmd/gotk/reconcile_kustomization.go
@@ -26,6 +26,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/wait"
@@ -68,7 +69,7 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/reconcile_receiver.go b/cmd/gotk/reconcile_receiver.go
index 9b151ab7..3022edba 100644
--- a/cmd/gotk/reconcile_receiver.go
+++ b/cmd/gotk/reconcile_receiver.go
@@ -19,9 +19,11 @@ package main
 import (
 	"context"
 	"fmt"
-	"github.com/fluxcd/pkg/apis/meta"
 	"time"
 
+	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
+
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/wait"
@@ -52,7 +54,7 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/reconcile_source_bucket.go b/cmd/gotk/reconcile_source_bucket.go
index c823806f..70adc7ec 100644
--- a/cmd/gotk/reconcile_source_bucket.go
+++ b/cmd/gotk/reconcile_source_bucket.go
@@ -19,9 +19,11 @@ package main
 import (
 	"context"
 	"fmt"
-	"github.com/fluxcd/pkg/apis/meta"
 	"time"
 
+	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
+
 	"github.com/spf13/cobra"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
@@ -54,7 +56,7 @@ func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/reconcile_source_git.go b/cmd/gotk/reconcile_source_git.go
index b586861f..91632ab8 100644
--- a/cmd/gotk/reconcile_source_git.go
+++ b/cmd/gotk/reconcile_source_git.go
@@ -19,9 +19,11 @@ package main
 import (
 	"context"
 	"fmt"
-	"github.com/fluxcd/pkg/apis/meta"
 	"time"
 
+	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
+
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/wait"
@@ -52,7 +54,7 @@ func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/reconcile_source_helm.go b/cmd/gotk/reconcile_source_helm.go
index 3f1fe13a..ea2ed6e7 100644
--- a/cmd/gotk/reconcile_source_helm.go
+++ b/cmd/gotk/reconcile_source_helm.go
@@ -19,9 +19,11 @@ package main
 import (
 	"context"
 	"fmt"
-	"github.com/fluxcd/pkg/apis/meta"
 	"time"
 
+	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
+
 	"github.com/spf13/cobra"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
@@ -54,7 +56,7 @@ func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/resume_alert.go b/cmd/gotk/resume_alert.go
index 7624a45c..34f05418 100644
--- a/cmd/gotk/resume_alert.go
+++ b/cmd/gotk/resume_alert.go
@@ -19,7 +19,9 @@ package main
 import (
 	"context"
 	"fmt"
+
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	"github.com/spf13/cobra"
 	corev1 "k8s.io/api/core/v1"
@@ -54,7 +56,7 @@ func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/resume_helmrelease.go b/cmd/gotk/resume_helmrelease.go
index 46f9c4ba..7a959560 100644
--- a/cmd/gotk/resume_helmrelease.go
+++ b/cmd/gotk/resume_helmrelease.go
@@ -19,7 +19,9 @@ package main
 import (
 	"context"
 	"fmt"
+
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	"github.com/spf13/cobra"
 	corev1 "k8s.io/api/core/v1"
@@ -55,7 +57,7 @@ func resumeHrCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/resume_kustomization.go b/cmd/gotk/resume_kustomization.go
index b683defb..2ac3bc3c 100644
--- a/cmd/gotk/resume_kustomization.go
+++ b/cmd/gotk/resume_kustomization.go
@@ -19,7 +19,9 @@ package main
 import (
 	"context"
 	"fmt"
+
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
 	"github.com/spf13/cobra"
@@ -54,7 +56,7 @@ func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/resume_receiver.go b/cmd/gotk/resume_receiver.go
index 25c40daa..f2f4ff74 100644
--- a/cmd/gotk/resume_receiver.go
+++ b/cmd/gotk/resume_receiver.go
@@ -19,7 +19,9 @@ package main
 import (
 	"context"
 	"fmt"
+
 	"github.com/fluxcd/pkg/apis/meta"
+	"github.com/fluxcd/toolkit/internal/utils"
 
 	"github.com/spf13/cobra"
 	corev1 "k8s.io/api/core/v1"
@@ -54,7 +56,7 @@ func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/suspend_alert.go b/cmd/gotk/suspend_alert.go
index 9e7a2388..cfbe03f9 100644
--- a/cmd/gotk/suspend_alert.go
+++ b/cmd/gotk/suspend_alert.go
@@ -24,6 +24,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var suspendAlertCmd = &cobra.Command{
@@ -49,7 +50,7 @@ func suspendAlertCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/suspend_helmrelease.go b/cmd/gotk/suspend_helmrelease.go
index 0763b74d..be6dedf8 100644
--- a/cmd/gotk/suspend_helmrelease.go
+++ b/cmd/gotk/suspend_helmrelease.go
@@ -24,6 +24,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 
 	helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var suspendHrCmd = &cobra.Command{
@@ -50,7 +51,7 @@ func suspendHrCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/suspend_kustomization.go b/cmd/gotk/suspend_kustomization.go
index 3b0c0d9a..7b416fdc 100644
--- a/cmd/gotk/suspend_kustomization.go
+++ b/cmd/gotk/suspend_kustomization.go
@@ -19,7 +19,9 @@ package main
 import (
 	"context"
 	"fmt"
+
 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
 )
@@ -48,7 +50,7 @@ func suspendKsCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/suspend_receiver.go b/cmd/gotk/suspend_receiver.go
index bb9d0c0b..893c8db3 100644
--- a/cmd/gotk/suspend_receiver.go
+++ b/cmd/gotk/suspend_receiver.go
@@ -24,6 +24,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 
 	notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var suspendReceiverCmd = &cobra.Command{
@@ -49,7 +50,7 @@ func suspendReceiverCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/gotk/uninstall.go b/cmd/gotk/uninstall.go
index 89e1aa97..789c966c 100644
--- a/cmd/gotk/uninstall.go
+++ b/cmd/gotk/uninstall.go
@@ -27,6 +27,7 @@ import (
 	helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
 	sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
+	"github.com/fluxcd/toolkit/internal/utils"
 )
 
 var uninstallCmd = &cobra.Command{
@@ -66,7 +67,7 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	kubeClient, err := utils.kubeClient(kubeconfig)
+	kubeClient, err := utils.KubeClient(kubeconfig)
 	if err != nil {
 		return err
 	}
@@ -111,7 +112,7 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
 			if uninstallDryRun {
 				kubectlArgs = append(kubectlArgs, dryRun)
 			}
-			if _, err := utils.execKubectlCommand(ctx, ModeOS, kubectlArgs...); err != nil {
+			if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...); err != nil {
 				return fmt.Errorf("uninstall failed: %w", err)
 			}
 		}
@@ -135,7 +136,7 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error {
 		if uninstallDryRun {
 			kubectlArgs = append(kubectlArgs, dryRun)
 		}
-		if _, err := utils.execKubectlCommand(ctx, ModeOS, kubectlArgs...); err != nil {
+		if _, err := utils.ExecKubectlCommand(ctx, utils.ModeOS, kubectlArgs...); err != nil {
 			return fmt.Errorf("uninstall failed: %w", err)
 		}
 	}
diff --git a/internal/flags/arch.go b/internal/flags/arch.go
new file mode 100644
index 00000000..543ef444
--- /dev/null
+++ b/internal/flags/arch.go
@@ -0,0 +1,54 @@
+/*
+Copyright 2020 The Flux CD contributors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/fluxcd/toolkit/internal/utils"
+)
+
+var supportedArchs = []string{"amd64", "arm", "arm64"}
+
+type Arch string
+
+func (a *Arch) String() string {
+	return string(*a)
+}
+
+func (a *Arch) Set(str string) error {
+	if strings.TrimSpace(str) == "" {
+		return fmt.Errorf("no arch given, must be one of: %s",
+			strings.Join(supportedArchs, ", "))
+	}
+	if !utils.ContainsItemString(supportedArchs, str) {
+		return fmt.Errorf("unsupported arch '%s', must be one of: %s",
+			str, strings.Join(supportedArchs, ", "))
+
+	}
+	*a = Arch(str)
+	return nil
+}
+
+func (a *Arch) Type() string {
+	return "arch"
+}
+
+func (a *Arch) Description() string {
+	return fmt.Sprintf("cluster architecture, available options are: (%s)", strings.Join(supportedArchs, ", "))
+}
diff --git a/cmd/gotk/flags.go b/internal/flags/ecdsa_curve.go
similarity index 51%
rename from cmd/gotk/flags.go
rename to internal/flags/ecdsa_curve.go
index 991aa730..a67e55ef 100644
--- a/cmd/gotk/flags.go
+++ b/internal/flags/ecdsa_curve.go
@@ -14,79 +14,15 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package main
+package flags
 
 import (
 	"crypto/elliptic"
 	"fmt"
 	"sort"
-	"strconv"
 	"strings"
 )
 
-var supportedPublicKeyAlgorithms = []string{"rsa", "ecdsa", "ed25519"}
-
-type PublicKeyAlgorithm string
-
-func (a *PublicKeyAlgorithm) String() string {
-	return string(*a)
-}
-
-func (a *PublicKeyAlgorithm) Set(str string) error {
-	if strings.TrimSpace(str) == "" {
-		return fmt.Errorf("no public key algorithm given, must be one of: %s",
-			strings.Join(supportedPublicKeyAlgorithms, ", "))
-	}
-	for _, v := range supportedPublicKeyAlgorithms {
-		if str == v {
-			*a = PublicKeyAlgorithm(str)
-			return nil
-		}
-	}
-	return fmt.Errorf("unsupported public key algorithm '%s', must be one of: %s",
-		str, strings.Join(supportedPublicKeyAlgorithms, ", "))
-}
-
-func (a *PublicKeyAlgorithm) Type() string {
-	return "publicKeyAlgorithm"
-}
-
-func (a *PublicKeyAlgorithm) Description() string {
-	return fmt.Sprintf("SSH public key algorithm (%s)", strings.Join(supportedPublicKeyAlgorithms, ", "))
-}
-
-var defaultRSAKeyBits = 2048
-
-type RSAKeyBits int
-
-func (b *RSAKeyBits) String() string {
-	return strconv.Itoa(int(*b))
-}
-
-func (b *RSAKeyBits) Set(str string) error {
-	if strings.TrimSpace(str) == "" {
-		*b = RSAKeyBits(defaultRSAKeyBits)
-		return nil
-	}
-	bits, err := strconv.Atoi(str)
-	if err != nil {
-		return err
-	}
-	if bits%8 != 0 {
-		return fmt.Errorf("RSA key bit size should be a multiples of 8")
-	}
-	*b = RSAKeyBits(bits)
-	return nil
-}
-
-func (b *RSAKeyBits) Type() string {
-	return "rsaKeyBits"
-}
-
-func (b *RSAKeyBits) Description() string {
-	return "SSH RSA public key bit size (multiplies of 8)"
-}
-
 type ECDSACurve struct {
 	elliptic.Curve
 }
diff --git a/internal/flags/log_level.go b/internal/flags/log_level.go
new file mode 100644
index 00000000..37d2a6eb
--- /dev/null
+++ b/internal/flags/log_level.go
@@ -0,0 +1,54 @@
+/*
+Copyright 2020 The Flux CD contributors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/fluxcd/toolkit/internal/utils"
+)
+
+var supportedLogLevels = []string{"debug", "info", "error"}
+
+type LogLevel string
+
+func (l *LogLevel) String() string {
+	return string(*l)
+}
+
+func (l *LogLevel) Set(str string) error {
+	if strings.TrimSpace(str) == "" {
+		return fmt.Errorf("no log level given, must be one of: %s",
+			strings.Join(supportedLogLevels, ", "))
+	}
+	if !utils.ContainsItemString(supportedLogLevels, str) {
+		return fmt.Errorf("unsupported log level '%s', must be one of: %s",
+			str, strings.Join(supportedLogLevels, ", "))
+
+	}
+	*l = LogLevel(str)
+	return nil
+}
+
+func (l *LogLevel) Type() string {
+	return "logLevel"
+}
+
+func (l *LogLevel) Description() string {
+	return fmt.Sprintf("log level, available options are: (%s)", strings.Join(supportedArchs, ", "))
+}
diff --git a/internal/flags/public_key_algorithm.go b/internal/flags/public_key_algorithm.go
new file mode 100644
index 00000000..545a627b
--- /dev/null
+++ b/internal/flags/public_key_algorithm.go
@@ -0,0 +1,53 @@
+/*
+Copyright 2020 The Flux CD contributors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"fmt"
+	"strings"
+)
+
+var supportedPublicKeyAlgorithms = []string{"rsa", "ecdsa", "ed25519"}
+
+type PublicKeyAlgorithm string
+
+func (a *PublicKeyAlgorithm) String() string {
+	return string(*a)
+}
+
+func (a *PublicKeyAlgorithm) Set(str string) error {
+	if strings.TrimSpace(str) == "" {
+		return fmt.Errorf("no public key algorithm given, must be one of: %s",
+			strings.Join(supportedPublicKeyAlgorithms, ", "))
+	}
+	for _, v := range supportedPublicKeyAlgorithms {
+		if str == v {
+			*a = PublicKeyAlgorithm(str)
+			return nil
+		}
+	}
+	return fmt.Errorf("unsupported public key algorithm '%s', must be one of: %s",
+		str, strings.Join(supportedPublicKeyAlgorithms, ", "))
+}
+
+func (a *PublicKeyAlgorithm) Type() string {
+	return "publicKeyAlgorithm"
+}
+
+func (a *PublicKeyAlgorithm) Description() string {
+	return fmt.Sprintf("SSH public key algorithm (%s)", strings.Join(supportedPublicKeyAlgorithms, ", "))
+}
diff --git a/internal/flags/rsa_key_bits.go b/internal/flags/rsa_key_bits.go
new file mode 100644
index 00000000..6ba14290
--- /dev/null
+++ b/internal/flags/rsa_key_bits.go
@@ -0,0 +1,55 @@
+/*
+Copyright 2020 The Flux CD contributors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+var defaultRSAKeyBits = 2048
+
+type RSAKeyBits int
+
+func (b *RSAKeyBits) String() string {
+	return strconv.Itoa(int(*b))
+}
+
+func (b *RSAKeyBits) Set(str string) error {
+	if strings.TrimSpace(str) == "" {
+		*b = RSAKeyBits(defaultRSAKeyBits)
+		return nil
+	}
+	bits, err := strconv.Atoi(str)
+	if err != nil {
+		return err
+	}
+	if bits%8 != 0 {
+		return fmt.Errorf("RSA key bit size should be a multiples of 8")
+	}
+	*b = RSAKeyBits(bits)
+	return nil
+}
+
+func (b *RSAKeyBits) Type() string {
+	return "rsaKeyBits"
+}
+
+func (b *RSAKeyBits) Description() string {
+	return "SSH RSA public key bit size (multiplies of 8)"
+}
diff --git a/cmd/gotk/utils.go b/internal/utils/utils.go
similarity index 87%
rename from cmd/gotk/utils.go
rename to internal/utils/utils.go
index 45c01f3a..7b53de9c 100644
--- a/cmd/gotk/utils.go
+++ b/internal/utils/utils.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package main
+package utils
 
 import (
 	"bufio"
@@ -60,7 +60,7 @@ const (
 	ModeCapture  ExecMode = "capture.stderr|stdout"
 )
 
-func (*Utils) execKubectlCommand(ctx context.Context, mode ExecMode, args ...string) (string, error) {
+func ExecKubectlCommand(ctx context.Context, mode ExecMode, args ...string) (string, error) {
 	var stdoutBuf, stderrBuf bytes.Buffer
 
 	c := exec.CommandContext(ctx, "kubectl", args...)
@@ -94,7 +94,7 @@ func (*Utils) execKubectlCommand(ctx context.Context, mode ExecMode, args ...str
 	return "", nil
 }
 
-func (*Utils) execTemplate(obj interface{}, tmpl, filename string) error {
+func ExecTemplate(obj interface{}, tmpl, filename string) error {
 	t, err := template.New("tmpl").Parse(tmpl)
 	if err != nil {
 		return err
@@ -124,8 +124,8 @@ func (*Utils) execTemplate(obj interface{}, tmpl, filename string) error {
 	return file.Sync()
 }
 
-func (*Utils) kubeClient(kubeConfigPath string) (client.Client, error) {
-	configFiles := utils.splitKubeConfigPath(kubeConfigPath)
+func KubeClient(kubeConfigPath string) (client.Client, error) {
+	configFiles := SplitKubeConfigPath(kubeConfigPath)
 	cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
 		&clientcmd.ClientConfigLoadingRules{Precedence: configFiles},
 		&clientcmd.ConfigOverrides{}).ClientConfig()
@@ -151,11 +151,11 @@ func (*Utils) kubeClient(kubeConfigPath string) (client.Client, error) {
 	return kubeClient, nil
 }
 
-// splitKubeConfigPath splits the given KUBECONFIG path based on the runtime OS
+// SplitKubeConfigPath splits the given KUBECONFIG path based on the runtime OS
 // target.
 //
 // Ref: https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#the-kubeconfig-environment-variable
-func (*Utils) splitKubeConfigPath(path string) []string {
+func SplitKubeConfigPath(path string) []string {
 	var sep string
 	switch runtime.GOOS {
 	case "windows":
@@ -166,7 +166,7 @@ func (*Utils) splitKubeConfigPath(path string) []string {
 	return strings.Split(path, sep)
 }
 
-func (*Utils) writeFile(content, filename string) error {
+func WriteFile(content, filename string) error {
 	file, err := os.Create(filename)
 	if err != nil {
 		return err
@@ -181,7 +181,7 @@ func (*Utils) writeFile(content, filename string) error {
 	return file.Sync()
 }
 
-func (*Utils) copyFile(src, dst string) error {
+func CopyFile(src, dst string) error {
 	in, err := os.Open(src)
 	if err != nil {
 		return err
@@ -201,7 +201,7 @@ func (*Utils) copyFile(src, dst string) error {
 	return out.Close()
 }
 
-func (*Utils) containsItemString(s []string, e string) bool {
+func ContainsItemString(s []string, e string) bool {
 	for _, a := range s {
 		if a == e {
 			return true
@@ -210,7 +210,7 @@ func (*Utils) containsItemString(s []string, e string) bool {
 	return false
 }
 
-func (*Utils) parseObjectKindName(input string) (string, string) {
+func ParseObjectKindName(input string) (string, string) {
 	kind := ""
 	name := input
 	parts := strings.Split(input, "/")
@@ -221,7 +221,7 @@ func (*Utils) parseObjectKindName(input string) (string, string) {
 	return kind, name
 }
 
-func (*Utils) makeDependsOn(deps []string) []dependency.CrossNamespaceDependencyReference {
+func MakeDependsOn(deps []string) []dependency.CrossNamespaceDependencyReference {
 	refs := []dependency.CrossNamespaceDependencyReference{}
 	for _, dep := range deps {
 		parts := strings.Split(dep, "/")
@@ -241,9 +241,9 @@ func (*Utils) makeDependsOn(deps []string) []dependency.CrossNamespaceDependency
 	return refs
 }
 
-// generateKustomizationYaml is the equivalent of running
+// GenerateKustomizationYaml is the equivalent of running
 // 'kustomize create --autodetect' in the specified dir
-func (*Utils) generateKustomizationYaml(dirPath string) error {
+func GenerateKustomizationYaml(dirPath string) error {
 	fs := filesys.MakeFsOnDisk()
 	kfile := filepath.Join(dirPath, "kustomization.yaml")
 
@@ -321,7 +321,7 @@ func (*Utils) generateKustomizationYaml(dirPath string) error {
 	return nil
 }
 
-func (*Utils) printTable(writer io.Writer, header []string, rows [][]string) {
+func PrintTable(writer io.Writer, header []string, rows [][]string) {
 	table := tablewriter.NewWriter(writer)
 	table.SetHeader(header)
 	table.SetAutoWrapText(false)
-- 
GitLab