From 50fcd1826a878f604d96ea6f74d275f697eeae35 Mon Sep 17 00:00:00 2001 From: Ashwathi Shiva Date: Mon, 25 May 2020 09:21:03 -0400 Subject: [PATCH 1/9] fixed test for adding preflight-mongo image --- pkg/qliksense/docker_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/qliksense/docker_test.go b/pkg/qliksense/docker_test.go index 0110c4bb..7ead8cc8 100644 --- a/pkg/qliksense/docker_test.go +++ b/pkg/qliksense/docker_test.go @@ -247,7 +247,7 @@ spec: if !haveMatchingImage(func(image string) bool { return image == "qlik-docker-oss.bintray.io/preflight-mongo" }) { - t.Fatal("expected to find the mongo Preflight image in the list, but it wasn't there") + t.Fatal("expected to find the preflight-mongo image in the list, but it wasn't there") } } From 48849b648994ba7cdec501d984c994d7e14a4b56 Mon Sep 17 00:00:00 2001 From: Ashwathi Shiva Date: Thu, 28 May 2020 20:25:55 -0400 Subject: [PATCH 2/9] initial commit: retrieve logs from failed init containers working, refactored code --- cmd/qliksense/postflight.go | 60 ++ cmd/qliksense/preflight.go | 49 +- cmd/qliksense/root.go | 8 +- go.sum | 5 +- pkg/api/clientgo_utils.go | 866 ++++++++++++++++++++++++++ pkg/postflight/db_migration_check.go | 41 ++ pkg/postflight/postflight_utils.go | 16 + pkg/preflight/deployability.go | 67 +- pkg/preflight/dns_check.go | 39 +- pkg/preflight/mongo_check.go | 40 +- pkg/preflight/preflight_utils.go | 719 +-------------------- pkg/preflight/preflight_utils_test.go | 43 -- pkg/preflight/role_check.go | 66 +- pkg/preflight/version_check.go | 12 +- 14 files changed, 1150 insertions(+), 881 deletions(-) create mode 100644 cmd/qliksense/postflight.go create mode 100644 pkg/api/clientgo_utils.go create mode 100644 pkg/postflight/db_migration_check.go create mode 100644 pkg/postflight/postflight_utils.go delete mode 100644 pkg/preflight/preflight_utils_test.go diff --git a/cmd/qliksense/postflight.go b/cmd/qliksense/postflight.go new file mode 100644 index 00000000..095b55e9 --- /dev/null +++ b/cmd/qliksense/postflight.go @@ -0,0 +1,60 @@ +package main + +import ( + "fmt" + + . "github.com/logrusorgru/aurora" + ansi "github.com/mattn/go-colorable" + "github.com/qlik-oss/sense-installer/pkg/api" + postflight "github.com/qlik-oss/sense-installer/pkg/postflight" + "github.com/qlik-oss/sense-installer/pkg/qliksense" + "github.com/spf13/cobra" +) + +func postflightCmd(q *qliksense.Qliksense) *cobra.Command { + postflightOpts := &postflight.PostflightOptions{} + var postflightCmd = &cobra.Command{ + Use: "postflight", + Short: "perform postflight checks on the cluster", + Long: `perform postflight checks on the cluster`, + Example: `qliksense postflight `, + } + f := postflightCmd.Flags() + f.BoolVarP(&postflightOpts.Verbose, "verbose", "v", false, "verbose mode") + return postflightCmd +} + +func pfMigrationCheck(q *qliksense.Qliksense) *cobra.Command { + out := ansi.NewColorableStdout() + postflightOpts := &postflight.PostflightOptions{} + var postflightMigrationCmd = &cobra.Command{ + Use: "db-migration-check", + Short: "check mongodb migration status on the cluster", + Long: `check mongodb migration status on the cluster`, + Example: `qliksense postflight db-migration-check`, + RunE: func(cmd *cobra.Command, args []string) error { + pf := &postflight.QliksensePostflight{Q: q, P: postflightOpts} + + // Postflight db_migration_check + namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() + if err != nil { + fmt.Fprintf(out, "%s\n", Red("Postflight db_migration_check FAILED")) + fmt.Printf("Error: %v\n", err) + return nil + } + if namespace == "" { + namespace = "default" + } + if err = pf.DbMigrationCheck(namespace, kubeConfigContents); err != nil { + fmt.Fprintf(out, "%s\n", Red("Postflight db_migration_check FAILED")) + fmt.Printf("Error: %v\n", err) + return nil + } + fmt.Fprintf(out, "%s\n", Green("Postflight db_migration_check PASSED")) + return nil + }, + } + f := postflightMigrationCmd.Flags() + f.BoolVarP(&postflightOpts.Verbose, "verbose", "v", false, "verbose mode") + return postflightMigrationCmd +} diff --git a/cmd/qliksense/preflight.go b/cmd/qliksense/preflight.go index 9f0eb4e8..5ef9c164 100644 --- a/cmd/qliksense/preflight.go +++ b/cmd/qliksense/preflight.go @@ -5,6 +5,7 @@ import ( . "github.com/logrusorgru/aurora" ansi "github.com/mattn/go-colorable" + "github.com/qlik-oss/sense-installer/pkg/api" "github.com/qlik-oss/sense-installer/pkg/preflight" "github.com/qlik-oss/sense-installer/pkg/qliksense" @@ -37,10 +38,10 @@ func pfDnsCheckCmd(q *qliksense.Qliksense) *cobra.Command { Long: `perform preflight dns check to check DNS connectivity status in the cluster`, Example: `qliksense preflight dns`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight DNS check - namespace, kubeConfigContents, err := preflight.InitPreflight() + namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight DNS check FAILED")) fmt.Printf("Error: %v\n", err) @@ -75,10 +76,10 @@ func pfK8sVersionCheckCmd(q *qliksense.Qliksense) *cobra.Command { Long: `check minimum valid kubernetes version on the cluster`, Example: `qliksense preflight kube-version`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight Kubernetes minimum version check - namespace, kubeConfigContents, err := preflight.InitPreflight() + namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight kubernetes minimum version check FAILED")) fmt.Printf("Error: %v\n", err) @@ -111,11 +112,11 @@ func pfAllChecksCmd(q *qliksense.Qliksense) *cobra.Command { Long: `perform all preflight checks on the target cluster`, Example: `qliksense preflight all`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight run all checks fmt.Printf("Running all preflight checks...\n\n") - namespace, kubeConfigContents, err := preflight.InitPreflight() + namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Unable to run the preflight checks suite")) fmt.Printf("Error: %v\n", err) @@ -151,10 +152,10 @@ func pfDeploymentCheckCmd(q *qliksense.Qliksense) *cobra.Command { Long: `perform preflight deployment check to ensure that we can create deployments in the cluster`, Example: `qliksense preflight deployment`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight deployments check - namespace, kubeConfigContents, err := preflight.InitPreflight() + namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED")) fmt.Printf("Error: %v\n", err) @@ -189,10 +190,10 @@ func pfServiceCheckCmd(q *qliksense.Qliksense) *cobra.Command { Long: `perform preflight service check to ensure that we are able to create services in the cluster`, Example: `qliksense preflight service`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight service check - namespace, kubeConfigContents, err := preflight.InitPreflight() + namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED")) fmt.Printf("Error: %v\n", err) @@ -228,10 +229,10 @@ func pfPodCheckCmd(q *qliksense.Qliksense) *cobra.Command { Long: `perform preflight pod check to ensure we can create pods in the cluster`, Example: `qliksense preflight pod`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight pod check - namespace, kubeConfigContents, err := preflight.InitPreflight() + namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED")) fmt.Printf("Error: %v\n", err) @@ -266,10 +267,10 @@ func pfCreateRoleCheckCmd(q *qliksense.Qliksense) *cobra.Command { Long: `perform preflight role check to ensure we are able to create a role in the cluster`, Example: `qliksense preflight createRole`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight role check - namespace, _, err := preflight.InitPreflight() + namespace, _, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED")) fmt.Printf("Error: %v\n", err) @@ -301,10 +302,10 @@ func pfCreateRoleBindingCheckCmd(q *qliksense.Qliksense) *cobra.Command { Long: `perform preflight rolebinding check to ensure we are able to create a rolebinding in the cluster`, Example: `qliksense preflight rolebinding`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight createRoleBinding check - namespace, _, err := preflight.InitPreflight() + namespace, _, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight rolebinding check FAILED")) fmt.Printf("Error: %v\n", err) @@ -336,10 +337,10 @@ func pfCreateServiceAccountCheckCmd(q *qliksense.Qliksense) *cobra.Command { Long: `perform preflight serviceaccount check to ensure we are able to create a service account in the cluster`, Example: `qliksense preflight serviceaccount`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight createServiceAccount check - namespace, _, err := preflight.InitPreflight() + namespace, _, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight ServiceAccount check FAILED")) fmt.Printf("Error: %v\n", err) @@ -370,10 +371,10 @@ func pfCreateAuthCheckCmd(q *qliksense.Qliksense) *cobra.Command { Long: `perform preflight authcheck that combines the role, rolebinding and serviceaccount checks`, Example: `qliksense preflight authcheck`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight authcheck - namespace, kubeConfigContents, err := preflight.InitPreflight() + namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight authcheck FAILED")) fmt.Printf("Error: %v\n", err) @@ -405,10 +406,10 @@ func pfMongoCheckCmd(q *qliksense.Qliksense) *cobra.Command { Long: `perform preflight mongo check to ensure we are able to connect to a mongodb instance in the cluster`, Example: `qliksense preflight mongo OR preflight mongo --url=`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight mongo check - namespace, kubeConfigContents, err := preflight.InitPreflight() + namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight mongo check FAILED")) fmt.Printf("Error: %v\n", err) @@ -445,10 +446,10 @@ func pfCleanupCmd(q *qliksense.Qliksense) *cobra.Command { Long: `perform preflight clean to ensure that all resources are cleared up in the cluster`, Example: `qliksense preflight clean`, RunE: func(cmd *cobra.Command, args []string) error { - qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts} + qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight clean - namespace, kubeConfigContents, err := preflight.InitPreflight() + namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight cleanup FAILED")) fmt.Printf("Error: %v\n", err) diff --git a/cmd/qliksense/root.go b/cmd/qliksense/root.go index 38fc5d33..d4ff689a 100644 --- a/cmd/qliksense/root.go +++ b/cmd/qliksense/root.go @@ -206,7 +206,7 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command { crdsCmd.AddCommand(crdsViewCmd(p)) crdsCmd.AddCommand(crdsInstallCmd(p)) - // add preflight command + // add preflight commands preflightCmd := preflightCmd(p) preflightCmd.AddCommand(pfDnsCheckCmd(p)) preflightCmd.AddCommand(pfK8sVersionCheckCmd(p)) @@ -224,6 +224,12 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command { cmd.AddCommand(preflightCmd) cmd.AddCommand(loadCrFile(p)) cmd.AddCommand((applyCmd(p))) + + // add postflight command + postflightCmd := postflightCmd(p) + postflightCmd.AddCommand(pfMigrationCheck(p)) + + cmd.AddCommand(postflightCmd) return cmd } diff --git a/go.sum b/go.sum index 29bd3fbc..3c129c0d 100644 --- a/go.sum +++ b/go.sum @@ -298,6 +298,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= @@ -368,6 +369,7 @@ github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2K github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= @@ -883,8 +885,6 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/qlik-oss/k-apis v0.1.1 h1:aZ4eTMB3mSn03Kuj7+RI0eFLkjK9+0qxADBioRb3qVA= -github.com/qlik-oss/k-apis v0.1.1/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U= github.com/qlik-oss/k-apis v0.1.2 h1:BBcrXl+NxdsvuRsZuJbvIFxMv5QIXqWBzhXOcr5KUX8= github.com/qlik-oss/k-apis v0.1.2/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U= github.com/qlik-oss/kustomize/api v0.3.3-0.20200424070349-b0312eb71568 h1:wHOUCGfnmgYqW3aCjuP3fXmB2T/uZXMvltO+F3us83E= @@ -1051,6 +1051,7 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2 h1:jxcFYjlkl8xaERsgLo+RNquI0epW6zuy/ZRQs6jnrFA= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/pkg/api/clientgo_utils.go b/pkg/api/clientgo_utils.go new file mode 100644 index 00000000..5b7e2394 --- /dev/null +++ b/pkg/api/clientgo_utils.go @@ -0,0 +1,866 @@ +package api + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "time" + + "github.com/mitchellh/go-homedir" + appsv1 "k8s.io/api/apps/v1" + apiv1 "k8s.io/api/core/v1" + "k8s.io/api/rbac/v1beta1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/retry" +) + +var gracePeriod int64 = 0 + +type ClientGoUtils struct { + Verbose bool +} + +func (p *ClientGoUtils) LogVerboseMessage(strMessage string, args ...interface{}) { + if p.Verbose || os.Getenv("QLIKSENSE_DEBUG") == "true" { + fmt.Printf(strMessage, args...) + } +} + +func int32Ptr(i int32) *int32 { return &i } + +func LoadKubeConfigAndNamespace() (string, []byte, error) { + fmt.Println("Reading .kube/config file...") + + homeDir, err := homedir.Dir() + if err != nil { + err = fmt.Errorf("Unable to deduce home dir") + return "", nil, err + } + fmt.Printf("Kube config location: %s\n\n", filepath.Join(homeDir, ".kube", "config")) + + kubeConfig := filepath.Join(homeDir, ".kube", "config") + kubeConfigContents, err := ioutil.ReadFile(kubeConfig) + if err != nil { + err = fmt.Errorf("Unable to deduce home dir") + return "", nil, err + } + + // retrieve namespace + namespace := GetKubectlNamespace() + // if namespace comes back empty, we will run checks in the default namespace + if namespace == "" { + namespace = "default" + } + + return namespace, kubeConfigContents, nil +} + +func RetryOnError(mf func() error) error { + return retry.OnError(wait.Backoff{ + Duration: 1 * time.Second, + Factor: 1, + Jitter: 0.1, + Steps: 5, + }, func(err error) bool { + return k8serrors.IsConflict(err) || k8serrors.IsGone(err) || k8serrors.IsServerTimeout(err) || + k8serrors.IsServiceUnavailable(err) || k8serrors.IsTimeout(err) || k8serrors.IsTooManyRequests(err) + }, mf) +} + +func GetK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clientset, *rest.Config, error) { + var clientConfig *rest.Config + var err error + if len(kubeconfig) == 0 { + clientConfig, err = rest.InClusterConfig() + if err != nil { + err = fmt.Errorf("Unable to load in-cluster kubeconfig: %w", err) + return nil, nil, err + } + } else { + config, err := clientcmd.Load(kubeconfig) + if err != nil { + err = fmt.Errorf("Unable to load kubeconfig: %w", err) + return nil, nil, err + } + if contextName != "" { + config.CurrentContext = contextName + } + clientConfig, err = clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig() + if err != nil { + err = fmt.Errorf("Unable to create client config from config: %w", err) + return nil, nil, err + } + } + clientset, err := kubernetes.NewForConfig(clientConfig) + if err != nil { + err = fmt.Errorf("Unable to create clientset: %w", err) + return nil, nil, err + } + return clientset, clientConfig, nil +} + +func (qp *ClientGoUtils) CreatePreflightTestDeployment(clientset *kubernetes.Clientset, namespace string, depName string, imageName string) (*appsv1.Deployment, error) { + deploymentsClient := clientset.AppsV1().Deployments(namespace) + deployment := &appsv1.Deployment{ + ObjectMeta: v1.ObjectMeta{ + Name: depName, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: int32Ptr(1), + Selector: &v1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "preflight-check", + }, + }, + Template: apiv1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: map[string]string{ + "app": "preflight-check", + "label": "preflight-check-label", + }, + }, + Spec: apiv1.PodSpec{ + Containers: []apiv1.Container{ + { + Name: "dep", + Image: imageName, + Ports: []apiv1.ContainerPort{ + { + Name: "http", + Protocol: apiv1.ProtocolTCP, + ContainerPort: 80, + }, + }, + }, + }, + }, + }, + }, + } + + // Create Deployment + var result *appsv1.Deployment + if err := RetryOnError(func() (err error) { + result, err = deploymentsClient.Create(deployment) + return err + }); err != nil { + err = fmt.Errorf("unable to create deployments in the %s namespace: %w", namespace, err) + return nil, err + } + qp.LogVerboseMessage("Created deployment %q\n", result.GetObjectMeta().GetName()) + + return deployment, nil +} + +func getDeployment(clientset *kubernetes.Clientset, namespace, depName string) (*appsv1.Deployment, error) { + deploymentsClient := clientset.AppsV1().Deployments(namespace) + var deployment *appsv1.Deployment + if err := RetryOnError(func() (err error) { + deployment, err = deploymentsClient.Get(depName, v1.GetOptions{}) + return err + }); err != nil { + err = fmt.Errorf("unable to get deployments in the %s namespace: %w", namespace, err) + LogDebugMessage("%v\n", err) + return nil, err + } + return deployment, nil +} + +func (qp *ClientGoUtils) DeleteDeployment(clientset *kubernetes.Clientset, namespace, name string) error { + deploymentsClient := clientset.AppsV1().Deployments(namespace) + // Create Deployment + deletePolicy := v1.DeletePropagationForeground + deleteOptions := v1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + GracePeriodSeconds: &gracePeriod, + } + + if err := RetryOnError(func() (err error) { + return deploymentsClient.Delete(name, &deleteOptions) + }); err != nil { + return err + } + if err := WaitForDeploymentToDelete(clientset, namespace, name); err != nil { + return err + } + qp.LogVerboseMessage("Deleted deployment: %s\n", name) + return nil +} + +func (qp *ClientGoUtils) CreatePreflightTestService(clientset *kubernetes.Clientset, namespace string, svcName string) (*apiv1.Service, error) { + iptr := int32Ptr(80) + servicesClient := clientset.CoreV1().Services(namespace) + service := &apiv1.Service{ + ObjectMeta: v1.ObjectMeta{ + Name: svcName, + Namespace: namespace, + Labels: map[string]string{ + "app": "preflight-check", + }, + }, + Spec: apiv1.ServiceSpec{ + Ports: []apiv1.ServicePort{ + {Name: "port1", + Port: *iptr, + }, + }, + Selector: map[string]string{ + "app": "preflight-check", + }, + ClusterIP: "", + }, + } + var result *apiv1.Service + if err := RetryOnError(func() (err error) { + result, err = servicesClient.Create(service) + return err + }); err != nil { + return nil, err + } + qp.LogVerboseMessage("Created service %q\n", result.GetObjectMeta().GetName()) + + return service, nil +} + +func GetService(clientset *kubernetes.Clientset, namespace, svcName string) (*apiv1.Service, error) { + servicesClient := clientset.CoreV1().Services(namespace) + var svc *apiv1.Service + if err := RetryOnError(func() (err error) { + svc, err = servicesClient.Get(svcName, v1.GetOptions{}) + return err + }); err != nil { + err = fmt.Errorf("unable to get services in the %s namespace: %w", namespace, err) + return nil, err + } + + return svc, nil +} + +func (qp *ClientGoUtils) DeleteService(clientset *kubernetes.Clientset, namespace, name string) error { + servicesClient := clientset.CoreV1().Services(namespace) + // Create Deployment + deletePolicy := v1.DeletePropagationForeground + deleteOptions := v1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + } + if err := RetryOnError(func() (err error) { + return servicesClient.Delete(name, &deleteOptions) + }); err != nil { + return err + } + qp.LogVerboseMessage("Deleted service: %s\n", name) + return nil +} + +func (qp *ClientGoUtils) DeletePod(clientset *kubernetes.Clientset, namespace, name string) error { + + podsClient := clientset.CoreV1().Pods(namespace) + deletePolicy := v1.DeletePropagationForeground + deleteOptions := v1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + GracePeriodSeconds: &gracePeriod, + } + if err := RetryOnError(func() (err error) { + return podsClient.Delete(name, &deleteOptions) + }); err != nil { + return err + } + if err := waitForPodToDelete(clientset, namespace, name); err != nil { + return err + } + qp.LogVerboseMessage("Deleted pod: %s\n", name) + return nil +} + +func (qp *ClientGoUtils) CreatePreflightTestPod(clientset *kubernetes.Clientset, namespace, podName, imageName string, secretNames map[string]string, commandToRun []string) (*apiv1.Pod, error) { + // build the pod definition we want to deploy + pod := &apiv1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Name: podName, + Namespace: namespace, + Labels: map[string]string{ + "app": "preflight", + }, + }, + Spec: apiv1.PodSpec{ + RestartPolicy: apiv1.RestartPolicyNever, + Containers: []apiv1.Container{ + { + Name: "cnt", + Image: imageName, + ImagePullPolicy: apiv1.PullIfNotPresent, + Command: commandToRun, + }, + }, + }, + } + if len(secretNames) > 0 { + for secretName, mountPath := range secretNames { + pod.Spec.Volumes = append(pod.Spec.Volumes, apiv1.Volume{ + Name: secretName, + VolumeSource: apiv1.VolumeSource{ + Secret: &apiv1.SecretVolumeSource{ + SecretName: secretName, + Items: []apiv1.KeyToPath{ + { + Key: secretName, + Path: filepath.Base(mountPath), + }, + }, + }, + }, + }) + if len(pod.Spec.Containers) > 0 { + pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, apiv1.VolumeMount{ + Name: secretName, + MountPath: filepath.Dir(mountPath), + ReadOnly: true, + }) + } + } + } + + // now create the pod in kubernetes cluster using the clientset + if err := RetryOnError(func() (err error) { + pod, err = clientset.CoreV1().Pods(namespace).Create(pod) + return err + }); err != nil { + return nil, err + } + qp.LogVerboseMessage("Created pod: %s\n", pod.Name) + return pod, nil +} + +func getPod(clientset *kubernetes.Clientset, namespace, podName string) (*apiv1.Pod, error) { + LogDebugMessage("Fetching pod: %s\n", podName) + var pod *apiv1.Pod + if err := RetryOnError(func() (err error) { + pod, err = clientset.CoreV1().Pods(namespace).Get(podName, v1.GetOptions{}) + return err + }); err != nil { + LogDebugMessage("%v\n", err) + return nil, err + } + return pod, nil +} + +func GetPodLogs(clientset *kubernetes.Clientset, pod *apiv1.Pod) (string, error) { + podLogOpts := apiv1.PodLogOptions{} + + LogDebugMessage("Retrieving logs for pod: %s namespace: %s\n", pod.GetName(), pod.Namespace) + req := clientset.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOpts) + podLogs, err := req.Stream() + if err != nil { + return "", err + } + defer podLogs.Close() + time.Sleep(15 * time.Second) + buf := new(bytes.Buffer) + _, err = io.Copy(buf, podLogs) + if err != nil { + return "", err + } + LogDebugMessage("Log from pod: %s\n", buf.String()) + return buf.String(), nil +} + +func waitForResource(checkFunc func() (interface{}, error), validateFunc func(interface{}) bool) error { + timeout := time.NewTicker(2 * time.Minute) + defer timeout.Stop() +OUT: + for { + r, err := checkFunc() + if err != nil { + return err + } + select { + case <-timeout.C: + break OUT + default: + if validateFunc(r) { + break OUT + } + } + time.Sleep(5 * time.Second) + } + return nil +} + +func WaitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDeployment *appsv1.Deployment) error { + var err error + depName := pfDeployment.GetName() + checkFunc := func() (interface{}, error) { + pfDeployment, err = getDeployment(clientset, namespace, depName) + if err != nil { + err = fmt.Errorf("unable to retrieve deployment: %s\n", depName) + return nil, err + } + return pfDeployment, nil + } + validateFunc := func(data interface{}) bool { + d := data.(*appsv1.Deployment) + return int(d.Status.ReadyReplicas) > 0 + } + if err := waitForResource(checkFunc, validateFunc); err != nil { + return err + } + if int(pfDeployment.Status.ReadyReplicas) == 0 { + err = fmt.Errorf("deployment took longer than expected to spin up pods") + return err + } + return nil +} + +func WaitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error { + var err error + if len(pod.Spec.Containers) == 0 { + err = fmt.Errorf("there are no containers in the pod") + return err + } + podName := pod.Name + checkFunc := func() (interface{}, error) { + pod, err = getPod(clientset, namespace, podName) + if err != nil { + err = fmt.Errorf("unable to retrieve %s pod by name", podName) + return nil, err + } + return pod, nil + } + validateFunc := func(data interface{}) bool { + po := data.(*apiv1.Pod) + return po.Status.Phase == apiv1.PodRunning || po.Status.Phase == apiv1.PodSucceeded || po.Status.Phase == apiv1.PodFailed + } + + if err := waitForResource(checkFunc, validateFunc); err != nil { + return err + } + if pod.Status.Phase != apiv1.PodRunning && pod.Status.Phase != apiv1.PodSucceeded && pod.Status.Phase != apiv1.PodFailed { + err = fmt.Errorf("container is taking much longer than expected") + return err + } + return nil +} + +func WaitForPodToDie(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error { + podName := pod.Name + checkFunc := func() (interface{}, error) { + po, err := getPod(clientset, namespace, podName) + if err != nil { + err = fmt.Errorf("unable to retrieve %s pod by name", podName) + return nil, err + } + return po, nil + } + validateFunc := func(r interface{}) bool { + po := r.(*apiv1.Pod) + return po.Status.Phase == apiv1.PodFailed || po.Status.Phase == apiv1.PodSucceeded + } + if err := waitForResource(checkFunc, validateFunc); err != nil { + return err + } + return nil +} + +func waitForPodToDelete(clientset *kubernetes.Clientset, namespace, podName string) error { + checkFunc := func() (interface{}, error) { + po, err := getPod(clientset, namespace, podName) + if err != nil { + return nil, err + } + return po, nil + } + validateFunc := func(po interface{}) bool { + return false + } + if err := waitForResource(checkFunc, validateFunc); err != nil { + return nil + } + err := fmt.Errorf("delete pod is taking unusually long") + return err +} + +func WaitForDeploymentToDelete(clientset *kubernetes.Clientset, namespace, deploymentName string) error { + checkFunc := func() (interface{}, error) { + dep, err := getDeployment(clientset, namespace, deploymentName) + if err != nil { + return nil, err + } + return dep, nil + } + validateFunc := func(po interface{}) bool { + return false + } + if err := waitForResource(checkFunc, validateFunc); err != nil { + return nil + } + err := fmt.Errorf("delete deployment is taking unusually long") + return err +} + +func (qp *ClientGoUtils) CreatePfRole(clientset *kubernetes.Clientset, namespace, roleName string) (*v1beta1.Role, error) { + // build the role defination we want to create + var role *v1beta1.Role + roleSpec := &v1beta1.Role{ + ObjectMeta: v1.ObjectMeta{ + Name: roleName, + Namespace: namespace, + Labels: map[string]string{ + "app": "preflight", + }, + }, + Rules: []v1beta1.PolicyRule{}, + } + + // now create the role in kubernetes cluster using the clientset + if err := RetryOnError(func() (err error) { + role, err = clientset.RbacV1beta1().Roles(namespace).Create(roleSpec) + return err + }); err != nil { + return nil, err + } + + qp.LogVerboseMessage("Created role: %s\n", role.Name) + + return role, nil +} + +func (qp *ClientGoUtils) DeleteRole(clientset *kubernetes.Clientset, namespace string, roleName string) error { + rolesClient := clientset.RbacV1beta1().Roles(namespace) + + deletePolicy := v1.DeletePropagationForeground + deleteOptions := v1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + } + err := rolesClient.Delete(roleName, &deleteOptions) + if err != nil { + log.Printf("Error: %v\n", err) + return err + } + qp.LogVerboseMessage("Deleted role: %s\n\n", roleName) + return nil +} + +func (qp *ClientGoUtils) CreatePfRoleBinding(clientset *kubernetes.Clientset, namespace, roleBindingName string) (*v1beta1.RoleBinding, error) { + var roleBinding *v1beta1.RoleBinding + // build the rolebinding defination we want to create + roleBindingSpec := &v1beta1.RoleBinding{ + ObjectMeta: v1.ObjectMeta{ + Name: roleBindingName, + Namespace: namespace, + Labels: map[string]string{ + "app": "preflight", + }, + }, + Subjects: []v1beta1.Subject{ + { + Kind: "ServiceAccount", + APIGroup: "", + Name: "preflight-check-subject", + Namespace: namespace, + }, + }, + RoleRef: v1beta1.RoleRef{ + APIGroup: "", + Kind: "Role", + Name: "preflight-check-roleref", + }, + } + + // now create the roleBinding in kubernetes cluster using the clientset + if err := RetryOnError(func() (err error) { + roleBinding, err = clientset.RbacV1beta1().RoleBindings(namespace).Create(roleBindingSpec) + return err + }); err != nil { + return nil, err + } + qp.LogVerboseMessage("Created RoleBinding: %s\n", roleBindingSpec.Name) + return roleBinding, nil +} + +func (qp *ClientGoUtils) DeleteRoleBinding(clientset *kubernetes.Clientset, namespace string, roleBindingName string) error { + roleBindingClient := clientset.RbacV1beta1().RoleBindings(namespace) + + deletePolicy := v1.DeletePropagationForeground + deleteOptions := v1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + } + err := roleBindingClient.Delete(roleBindingName, &deleteOptions) + if err != nil { + log.Printf("Error: %v\n", err) + return err + } + qp.LogVerboseMessage("Deleted RoleBinding: %s\n\n", roleBindingName) + return nil +} + +func (qp *ClientGoUtils) CreatePfServiceAccount(clientset *kubernetes.Clientset, namespace, serviceAccountName string) (*apiv1.ServiceAccount, error) { + var serviceAccount *apiv1.ServiceAccount + // build the serviceAccount defination we want to create + serviceAccountSpec := &apiv1.ServiceAccount{ + ObjectMeta: v1.ObjectMeta{ + Name: "preflight-check-test-serviceaccount", + Namespace: namespace, + Labels: map[string]string{ + "app": "preflight", + }, + }, + } + + // now create the serviceAccount in kubernetes cluster using the clientset + if err := RetryOnError(func() (err error) { + serviceAccount, err = clientset.CoreV1().ServiceAccounts(namespace).Create(serviceAccountSpec) + return err + }); err != nil { + return nil, err + } + qp.LogVerboseMessage("Created Service Account: %s\n", serviceAccountSpec.Name) + return serviceAccount, nil +} + +func (qp *ClientGoUtils) DeleteServiceAccount(clientset *kubernetes.Clientset, namespace string, serviceAccountName string) error { + serviceAccountClient := clientset.CoreV1().ServiceAccounts(namespace) + + deletePolicy := v1.DeletePropagationForeground + deleteOptions := v1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + } + err := serviceAccountClient.Delete(serviceAccountName, &deleteOptions) + if err != nil { + log.Printf("Error: %v\n", err) + return err + } + qp.LogVerboseMessage("Deleted ServiceAccount: %s\n\n", serviceAccountName) + return nil +} + +func (qp *ClientGoUtils) CreatePreflightTestSecret(clientset *kubernetes.Clientset, namespace, secretName string, secretData []byte) (*apiv1.Secret, error) { + var secret *apiv1.Secret + var err error + // build the secret defination we want to create + secretSpec := &apiv1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + Labels: map[string]string{ + "app": "preflight", + }, + }, + Data: map[string][]byte{ + secretName: secretData, + }, + } + + // now create the secret in kubernetes cluster using the clientset + if err = RetryOnError(func() (err error) { + secret, err = clientset.CoreV1().Secrets(namespace).Create(secretSpec) + return err + }); err != nil { + return nil, err + } + qp.LogVerboseMessage("Created Secret: %s\n", secret.Name) + return secret, nil +} + +func (qp *ClientGoUtils) DeleteK8sSecret(clientset *kubernetes.Clientset, namespace string, secretName string) error { + secretClient := clientset.CoreV1().Secrets(namespace) + + deletePolicy := v1.DeletePropagationForeground + deleteOptions := v1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + } + err := secretClient.Delete(secretName, &deleteOptions) + if err != nil { + return err + } + qp.LogVerboseMessage("Deleted Secret: %s\n", secretName) + return nil +} + +func CreateStatefulSet(clientset *kubernetes.Clientset, namespace string, statefulSetName string, imageName string) (*appsv1.StatefulSet, error) { + statefulSetsClient := clientset.AppsV1().StatefulSets(namespace) + statefulset := &appsv1.StatefulSet{ + ObjectMeta: v1.ObjectMeta{ + Name: statefulSetName, + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: int32Ptr(1), + Selector: &v1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "postflight-check", + }, + }, + Template: apiv1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: map[string]string{ + "app": "postflight-check", + "label": "postflight-check-label", + }, + }, + Spec: apiv1.PodSpec{ + InitContainers: []apiv1.Container{ + { + Name: "migration", + Image: "ubuntu", + ImagePullPolicy: apiv1.PullIfNotPresent, + // Command: []string{"bash", "-c", "for i in {1..10}; do echo \"from init container...\"; sleep 1; done"}, + Command: []string{"bash", "-c", "for i in {1..10}; do echo \"from init container...\"; sleep 1; exit 1; done"}, + }, + }, + Containers: []apiv1.Container{ + { + Name: "statefulset", + Image: imageName, + Ports: []apiv1.ContainerPort{ + { + Name: "http", + Protocol: apiv1.ProtocolTCP, + ContainerPort: 80, + }, + }, + }, + }, + }, + }, + }, + } + + // Create Deployment + var result *appsv1.StatefulSet + if err := RetryOnError(func() (err error) { + result, err = statefulSetsClient.Create(statefulset) + return err + }); err != nil { + err = fmt.Errorf("unable to create statefulsets in the %s namespace: %w", namespace, err) + return nil, err + } + fmt.Printf("Created statefulset %q\n", result.GetObjectMeta().GetName()) + + return statefulset, nil +} + +func GetPodsForStatefulset(clientset *kubernetes.Clientset, statefulset *appsv1.StatefulSet, namespace string) (*apiv1.PodList, error) { + set := labels.Set(statefulset.Spec.Template.Labels) + listOptions := v1.ListOptions{LabelSelector: set.AsSelector().String()} + pods, err := clientset.CoreV1().Pods(namespace).List(listOptions) + for _, pod := range pods.Items { + fmt.Fprintf(os.Stdout, "*********** pod name: %v\n", pod.Name) + } + return pods, err +} + +func waitForStatefulSet(clientset *kubernetes.Clientset, namespace string, pfStatefulset *appsv1.StatefulSet) error { + var err error + statefulsetName := pfStatefulset.GetName() + checkFunc := func() (interface{}, error) { + pfStatefulset, err = getStatefulset(clientset, namespace, statefulsetName) + if err != nil { + err = fmt.Errorf("unable to retrieve stateful set: %s\n", statefulsetName) + return nil, err + } + return pfStatefulset, nil + } + validateFunc := func(data interface{}) bool { + s := data.(*appsv1.StatefulSet) + return int(s.Status.ReadyReplicas) > 0 + } + if err := waitForResource(checkFunc, validateFunc); err != nil { + return err + } + if int(pfStatefulset.Status.ReadyReplicas) == 0 { + err = fmt.Errorf("deployment took longer than expected to spin up pods") + return err + } + return nil +} + +func getStatefulset(clientset *kubernetes.Clientset, namespace, statefulsetName string) (*appsv1.StatefulSet, error) { + statefulsetsClient := clientset.AppsV1().StatefulSets(namespace) + var statefulset *appsv1.StatefulSet + if err := RetryOnError(func() (err error) { + statefulset, err = statefulsetsClient.Get(statefulsetName, v1.GetOptions{}) + return err + }); err != nil { + err = fmt.Errorf("unable to get statefulsets in the %s namespace: %w", namespace, err) + fmt.Printf("%v\n", err) + return nil, err + } + return statefulset, nil +} + +func deleteStatefulSet(clientset *kubernetes.Clientset, namespace, name string) error { + statefulsetClient := clientset.AppsV1().StatefulSets(namespace) + + deletePolicy := v1.DeletePropagationForeground + deleteOptions := v1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + GracePeriodSeconds: &gracePeriod, + } + + if err := RetryOnError(func() (err error) { + return statefulsetClient.Delete(name, &deleteOptions) + }); err != nil { + return err + } + if err := waitForStatefulsetToDelete(clientset, namespace, name); err != nil { + return err + } + fmt.Printf("Deleted statefulset: %s\n", name) + return nil +} + +func waitForStatefulsetToDelete(clientset *kubernetes.Clientset, namespace, statefulsetName string) error { + checkFunc := func() (interface{}, error) { + statefulset, err := getStatefulset(clientset, namespace, statefulsetName) + if err != nil { + return nil, err + } + return statefulset, nil + } + validateFunc := func(po interface{}) bool { + return false + } + if err := waitForResource(checkFunc, validateFunc); err != nil { + return nil + } + err := fmt.Errorf("delete statefulset is taking unusually long") + return err +} + +func GetPodsAndPodLogsForFailedInitContainer(clientset *kubernetes.Clientset, lbls map[string]string, namespace, containerName string) error { + set := labels.Set(lbls) + listOptions := v1.ListOptions{LabelSelector: set.AsSelector().String()} + podList, err := clientset.CoreV1().Pods(namespace).List(listOptions) + if err != nil { + err = fmt.Errorf("unable to get podlist: %v", err) + fmt.Printf("%s\n", err) + } + fmt.Printf("%d Pods retrieved\n ", len(podList.Items)) + + for _, pod := range podList.Items { + fmt.Fprintf(os.Stdout, "*********** pod name: %v\n", pod.Name) + fmt.Printf("%d init containers retrieved\n", len(pod.Spec.InitContainers)) + for _, cs := range pod.Status.InitContainerStatuses { + if (cs.State.Terminated != nil && (cs.State.Terminated.Reason != "Completed" || cs.State.Terminated.ExitCode > 0)) || + (cs.LastTerminationState.Terminated != nil && (cs.LastTerminationState.Terminated.Reason != "Completed" || cs.LastTerminationState.Terminated.ExitCode > 0)) && (cs.Name == containerName) { + logs, err := GetPodLogs(clientset, &pod) + if err != nil { + err = fmt.Errorf("unable to get pod logs: %v", err) + fmt.Printf("%s\n", err) + return err + } + fmt.Printf("Retrieved logs from init container: %s\n%s", cs.Name, logs) + } + } + } + return nil +} diff --git a/pkg/postflight/db_migration_check.go b/pkg/postflight/db_migration_check.go new file mode 100644 index 00000000..38beea04 --- /dev/null +++ b/pkg/postflight/db_migration_check.go @@ -0,0 +1,41 @@ +package postflight + +import ( + "fmt" + + "github.com/qlik-oss/sense-installer/pkg/api" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const initContainerNameToCheck = "migration" + +func (qp *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigContents []byte) error { + + clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") + if err != nil { + err = fmt.Errorf("unable to create a kubernetes client: %v", err) + fmt.Printf("%s\n", err) + return err + } + + // Retrieve all deployments + deploymentsClient := clientset.AppsV1().Deployments(namespace) + deployments, err := deploymentsClient.List(v1.ListOptions{}) + fmt.Printf("Number of deployments found: %d\n", deployments.Size()) + for _, deployment := range deployments.Items { + fmt.Printf("Deployment name: %s\n", deployment.GetName()) + err = api.GetPodsAndPodLogsForFailedInitContainer(clientset, deployment.Spec.Template.Labels, namespace, initContainerNameToCheck) + } + + // retrieve all statefulsets + statefulsetsClient := clientset.AppsV1().StatefulSets(namespace) + statefulsets, err := statefulsetsClient.List(v1.ListOptions{}) + fmt.Printf("Number of statefulsets found: %d\n", statefulsets.Size()) + for _, statefulset := range statefulsets.Items { + fmt.Printf("Deployment name: %s\n", statefulset.GetName()) + err = api.GetPodsAndPodLogsForFailedInitContainer(clientset, statefulset.Spec.Template.Labels, namespace, initContainerNameToCheck) + } + + fmt.Printf("all done!\n") + return nil +} diff --git a/pkg/postflight/postflight_utils.go b/pkg/postflight/postflight_utils.go new file mode 100644 index 00000000..d20966b6 --- /dev/null +++ b/pkg/postflight/postflight_utils.go @@ -0,0 +1,16 @@ +package postflight + +import ( + "github.com/qlik-oss/sense-installer/pkg/api" + "github.com/qlik-oss/sense-installer/pkg/qliksense" +) + +type PostflightOptions struct { + Verbose bool +} + +type QliksensePostflight struct { + Q *qliksense.Qliksense + P *PostflightOptions + CG *api.ClientGoUtils +} diff --git a/pkg/preflight/deployability.go b/pkg/preflight/deployability.go index 5d926ce1..27709b53 100644 --- a/pkg/preflight/deployability.go +++ b/pkg/preflight/deployability.go @@ -3,11 +3,12 @@ package preflight import ( "fmt" + "github.com/qlik-oss/sense-installer/pkg/api" "k8s.io/client-go/kubernetes" ) func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigContents []byte, cleanup bool) error { - clientset, _, err := getK8SClientSet(kubeConfigContents, "") + clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("Kube config error: %v\n", err) return err @@ -15,62 +16,62 @@ func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigConten // Deployment check if !cleanup { - qp.P.LogVerboseMessage("Preflight deployment check: \n") - qp.P.LogVerboseMessage("--------------------------- \n") + qp.CG.LogVerboseMessage("Preflight deployment check: \n") + qp.CG.LogVerboseMessage("--------------------------- \n") } err = qp.checkPfDeployment(clientset, namespace, cleanup) if err != nil { - qp.P.LogVerboseMessage("Preflight Deployment check: FAILED\n") + qp.CG.LogVerboseMessage("Preflight Deployment check: FAILED\n") return err } if !cleanup { - qp.P.LogVerboseMessage("Completed preflight deployment check\n") + qp.CG.LogVerboseMessage("Completed preflight deployment check\n") } return nil } func (qp *QliksensePreflight) CheckService(namespace string, kubeConfigContents []byte, cleanup bool) error { - clientset, _, err := getK8SClientSet(kubeConfigContents, "") + clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("unable to create a kubernetes client: %v\n", err) return err } // Service check if !cleanup { - qp.P.LogVerboseMessage("Preflight service check: \n") - qp.P.LogVerboseMessage("------------------------ \n") + qp.CG.LogVerboseMessage("Preflight service check: \n") + qp.CG.LogVerboseMessage("------------------------ \n") } err = qp.checkPfService(clientset, namespace, cleanup) if err != nil { - qp.P.LogVerboseMessage("Preflight Service check: FAILED\n") + qp.CG.LogVerboseMessage("Preflight Service check: FAILED\n") return err } if !cleanup { - qp.P.LogVerboseMessage("Completed preflight service check\n") + qp.CG.LogVerboseMessage("Completed preflight service check\n") } return nil } func (qp *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []byte, cleanup bool) error { - clientset, _, err := getK8SClientSet(kubeConfigContents, "") + clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err) return err } // Pod check if !cleanup { - qp.P.LogVerboseMessage("Preflight pod check: \n") - qp.P.LogVerboseMessage("-------------------- \n") + qp.CG.LogVerboseMessage("Preflight pod check: \n") + qp.CG.LogVerboseMessage("-------------------- \n") } err = qp.checkPfPod(clientset, namespace, cleanup) if err != nil { - qp.P.LogVerboseMessage("Preflight Pod check: FAILED\n") + qp.CG.LogVerboseMessage("Preflight Pod check: FAILED\n") return err } if !cleanup { - qp.P.LogVerboseMessage("Completed preflight pod check\n") + qp.CG.LogVerboseMessage("Completed preflight pod check\n") } return nil } @@ -78,7 +79,7 @@ func (qp *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []by func (qp *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { // delete the pod we are going to create, if it already exists in the cluster podName := "pod-pf-check" - qp.deletePod(clientset, namespace, podName) + qp.CG.DeletePod(clientset, namespace, podName) if cleanup { return nil } @@ -88,50 +89,50 @@ func (qp *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namesp return err } // create a pod - pod, err := qp.createPreflightTestPod(clientset, namespace, podName, imageName, nil, commandToRun) + pod, err := qp.CG.CreatePreflightTestPod(clientset, namespace, podName, imageName, nil, commandToRun) if err != nil { err = fmt.Errorf("unable to create pod - %v\n", err) return err } - defer qp.deletePod(clientset, namespace, podName) + defer qp.CG.DeletePod(clientset, namespace, podName) - if err := waitForPod(clientset, namespace, pod); err != nil { + if err := api.WaitForPod(clientset, namespace, pod); err != nil { return err } - qp.P.LogVerboseMessage("Preflight pod creation check: PASSED\n") - qp.P.LogVerboseMessage("Cleaning up resources...\n") + qp.CG.LogVerboseMessage("Preflight pod creation check: PASSED\n") + qp.CG.LogVerboseMessage("Cleaning up resources...\n") return nil } func (qp *QliksensePreflight) checkPfService(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { // delete the service we are going to create, if it already exists in the cluster serviceName := "svc-pf-check" - qp.deleteService(clientset, namespace, serviceName) + qp.CG.DeleteService(clientset, namespace, serviceName) if cleanup { return nil } // creating service - pfService, err := qp.createPreflightTestService(clientset, namespace, serviceName) + pfService, err := qp.CG.CreatePreflightTestService(clientset, namespace, serviceName) if err != nil { err = fmt.Errorf("unable to create service - %v\n", err) return err } - defer qp.deleteService(clientset, namespace, serviceName) - _, err = getService(clientset, namespace, pfService.GetName()) + defer qp.CG.DeleteService(clientset, namespace, serviceName) + _, err = api.GetService(clientset, namespace, pfService.GetName()) if err != nil { err = fmt.Errorf("unable to retrieve service - %v\n", err) return err } - qp.P.LogVerboseMessage("Preflight service creation check: PASSED\n") - qp.P.LogVerboseMessage("Cleaning up resources...\n") + qp.CG.LogVerboseMessage("Preflight service creation check: PASSED\n") + qp.CG.LogVerboseMessage("Cleaning up resources...\n") return nil } func (qp *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { // delete the deployment we are going to create, if it already exists in the cluster depName := "deployment-preflight-check" - qp.deleteDeployment(clientset, namespace, depName) + qp.CG.DeleteDeployment(clientset, namespace, depName) if cleanup { return nil } @@ -141,16 +142,16 @@ func (qp *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset, if err != nil { return err } - pfDeployment, err := qp.createPreflightTestDeployment(clientset, namespace, depName, imageName) + pfDeployment, err := qp.CG.CreatePreflightTestDeployment(clientset, namespace, depName, imageName) if err != nil { err = fmt.Errorf("unable to create deployment - %v\n", err) return err } - defer qp.deleteDeployment(clientset, namespace, depName) - if err := waitForDeployment(clientset, namespace, pfDeployment); err != nil { + defer qp.CG.DeleteDeployment(clientset, namespace, depName) + if err := api.WaitForDeployment(clientset, namespace, pfDeployment); err != nil { return err } - qp.P.LogVerboseMessage("Preflight Deployment check: PASSED\n") - qp.P.LogVerboseMessage("Cleaning up resources...\n") + qp.CG.LogVerboseMessage("Preflight Deployment check: PASSED\n") + qp.CG.LogVerboseMessage("Cleaning up resources...\n") return nil } diff --git a/pkg/preflight/dns_check.go b/pkg/preflight/dns_check.go index 321ea246..9afcea26 100644 --- a/pkg/preflight/dns_check.go +++ b/pkg/preflight/dns_check.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/qlik-oss/sense-installer/pkg/api" "k8s.io/client-go/kubernetes" ) @@ -18,10 +19,10 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by podName := "pf-pod-1" if !cleanup { - qp.P.LogVerboseMessage("Preflight DNS check: \n") - qp.P.LogVerboseMessage("------------------- \n") + qp.CG.LogVerboseMessage("Preflight DNS check: \n") + qp.CG.LogVerboseMessage("------------------- \n") } - clientset, _, err := getK8SClientSet(kubeConfigContents, "") + clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("unable to create a kubernetes client: %v\n", err) return err @@ -38,24 +39,24 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by return err } - dnsDeployment, err := qp.createPreflightTestDeployment(clientset, namespace, depName, nginxImageName) + dnsDeployment, err := qp.CG.CreatePreflightTestDeployment(clientset, namespace, depName, nginxImageName) if err != nil { err = fmt.Errorf("unable to create deployment: %v\n", err) return err } - defer qp.deleteDeployment(clientset, namespace, depName) + defer qp.CG.DeleteDeployment(clientset, namespace, depName) - if err := waitForDeployment(clientset, namespace, dnsDeployment); err != nil { + if err := api.WaitForDeployment(clientset, namespace, dnsDeployment); err != nil { return err } // creating service - dnsService, err := qp.createPreflightTestService(clientset, namespace, serviceName) + dnsService, err := qp.CG.CreatePreflightTestService(clientset, namespace, serviceName) if err != nil { err = fmt.Errorf("unable to create service : %s, %s\n", serviceName, err) return err } - defer qp.deleteService(clientset, namespace, serviceName) + defer qp.CG.DeleteService(clientset, namespace, serviceName) // create a pod commandToRun := []string{"sh", "-c", "sleep 10; nc -z -v -w 1 " + dnsService.Name + " 80"} @@ -65,15 +66,15 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by return err } - dnsPod, err := qp.createPreflightTestPod(clientset, namespace, podName, netcatImageName, nil, commandToRun) + dnsPod, err := qp.CG.CreatePreflightTestPod(clientset, namespace, podName, netcatImageName, nil, commandToRun) if err != nil { err = fmt.Errorf("unable to create pod : %s, %s\n", podName, err) return err } - defer qp.deletePod(clientset, namespace, podName) + defer qp.CG.DeletePod(clientset, namespace, podName) - if err := waitForPod(clientset, namespace, dnsPod); err != nil { + if err := api.WaitForPod(clientset, namespace, dnsPod); err != nil { return err } if len(dnsPod.Spec.Containers) == 0 { @@ -81,30 +82,30 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by return err } - waitForPodToDie(clientset, namespace, dnsPod) + api.WaitForPodToDie(clientset, namespace, dnsPod) - logStr, err := getPodLogs(clientset, dnsPod) + logStr, err := api.GetPodLogs(clientset, dnsPod) if err != nil { err = fmt.Errorf("unable to execute dns check in the cluster: %v", err) return err } if strings.HasSuffix(strings.TrimSpace(logStr), "succeeded!") { - qp.P.LogVerboseMessage("Preflight DNS check: PASSED\n") + qp.CG.LogVerboseMessage("Preflight DNS check: PASSED\n") } else { err = fmt.Errorf("Expected response not found\n") return err } if !cleanup { - qp.P.LogVerboseMessage("Completed preflight DNS check\n") - qp.P.LogVerboseMessage("Cleaning up resources...\n") + qp.CG.LogVerboseMessage("Completed preflight DNS check\n") + qp.CG.LogVerboseMessage("Cleaning up resources...\n") } return nil } func (qp *QliksensePreflight) runDNSCleanup(clientset *kubernetes.Clientset, namespace, podName, serviceName, depName string) { - qp.deleteDeployment(clientset, namespace, depName) - qp.deletePod(clientset, namespace, podName) - qp.deleteService(clientset, namespace, serviceName) + qp.CG.DeleteDeployment(clientset, namespace, depName) + qp.CG.DeletePod(clientset, namespace, podName) + qp.CG.DeleteService(clientset, namespace, serviceName) } diff --git a/pkg/preflight/mongo_check.go b/pkg/preflight/mongo_check.go index 3542e908..817056de 100644 --- a/pkg/preflight/mongo_check.go +++ b/pkg/preflight/mongo_check.go @@ -22,8 +22,8 @@ const ( func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error { if !cleanup { - qp.P.LogVerboseMessage("Preflight mongodb check: \n") - qp.P.LogVerboseMessage("------------------------ \n") + qp.CG.LogVerboseMessage("Preflight mongodb check: \n") + qp.CG.LogVerboseMessage("------------------------ \n") } var currentCR *qapi.QliksenseCR var err error @@ -31,17 +31,17 @@ func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace st qConfig.SetNamespace(namespace) currentCR, err = qConfig.GetCurrentCR() if err != nil { - qp.P.LogVerboseMessage("Unable to retrieve current CR: %v\n", err) + qp.CG.LogVerboseMessage("Unable to retrieve current CR: %v\n", err) return err } decryptedCR, err := qConfig.GetDecryptedCr(currentCR) if err != nil { - qp.P.LogVerboseMessage("An error occurred while retrieving mongodbUrl from current CR: %v\n", err) + qp.CG.LogVerboseMessage("An error occurred while retrieving mongodbUrl from current CR: %v\n", err) return err } if preflightOpts.MongoOptions.MongodbUrl == "" && !cleanup { // infer mongoDbUrl from currentCR - qp.P.LogVerboseMessage("MongoDbUri is empty, infer from CR\n") + qp.CG.LogVerboseMessage("MongoDbUri is empty, infer from CR\n") preflightOpts.MongoOptions.MongodbUrl = strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("qliksense", "mongoDbUri")) } @@ -57,11 +57,11 @@ func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace st } if !cleanup { - qp.P.LogVerboseMessage("MongodbUrl: %s\n", preflightOpts.MongoOptions.MongodbUrl) + qp.CG.LogVerboseMessage("MongodbUrl: %s\n", preflightOpts.MongoOptions.MongodbUrl) // if mongoDbUrl is empty, abort check if preflightOpts.MongoOptions.MongodbUrl == "" { - qp.P.LogVerboseMessage("Mongodb Url is empty, hence aborting preflight check\n") + qp.CG.LogVerboseMessage("Mongodb Url is empty, hence aborting preflight check\n") return errors.New("MongoDbUrl is empty") } } @@ -71,7 +71,7 @@ func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace st } if !cleanup { - qp.P.LogVerboseMessage("Completed preflight mongodb check\n") + qp.CG.LogVerboseMessage("Completed preflight mongodb check\n") } return nil } @@ -79,7 +79,7 @@ func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace st func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error { caCertSecretName := "ca-certificates-crt" mongoPodName := "pf-mongo-pod" - clientset, _, err := getK8SClientSet(kubeConfigContents, "") + clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("unable to create a kubernetes client: %v\n", err) return err @@ -98,7 +98,7 @@ func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespac return err } - defer qp.deleteK8sSecret(clientset, namespace, caCertSecret.Name) + defer qp.CG.DeleteK8sSecret(clientset, namespace, caCertSecret.Name) secrets[caCertSecretName] = caCertMountPath } @@ -112,22 +112,22 @@ func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespac return err } api.LogDebugMessage("image name to be used: %s\n", imageName) - mongoPod, err := qp.createPreflightTestPod(clientset, namespace, mongoPodName, imageName, secrets, commandToRun) + mongoPod, err := qp.CG.CreatePreflightTestPod(clientset, namespace, mongoPodName, imageName, secrets, commandToRun) if err != nil { err = fmt.Errorf("unable to create pod : %v\n", err) return err } - defer qp.deletePod(clientset, namespace, mongoPodName) + defer qp.CG.DeletePod(clientset, namespace, mongoPodName) - if err := waitForPod(clientset, namespace, mongoPod); err != nil { + if err := api.WaitForPod(clientset, namespace, mongoPod); err != nil { return err } if len(mongoPod.Spec.Containers) == 0 { err := fmt.Errorf("there are no containers in the pod- %v\n", err) return err } - waitForPodToDie(clientset, namespace, mongoPod) - logStr, err := getPodLogs(clientset, mongoPod) + api.WaitForPodToDie(clientset, namespace, mongoPod) + logStr, err := api.GetPodLogs(clientset, mongoPod) if err != nil { err = fmt.Errorf("unable to execute mongo check in the cluster: %v\n", err) return err @@ -142,7 +142,7 @@ func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespac // check if connection succeeded stringToCheck := "qlik - connection succeeded!!" if strings.Contains(logStr, stringToCheck) { - qp.P.LogVerboseMessage("Preflight mongo check: PASSED\n") + qp.CG.LogVerboseMessage("Preflight mongo check: PASSED\n") } else { err = fmt.Errorf("Connection failed: %s\n", logStr) return err @@ -175,7 +175,7 @@ func (qp *QliksensePreflight) checkMongoVersion(logStr string) (bool, error) { return false, err } if currentMongoVersionSemver.GreaterThan(minMongoVersionSemver) || currentMongoVersionSemver.Equal(minMongoVersionSemver) { - qp.P.LogVerboseMessage("Current mongodb server version %s is greater than or equal to minimum required mongodb version: %s\n", currentMongoVersionSemver, minMongoVersionSemver) + qp.CG.LogVerboseMessage("Current mongodb server version %s is greater than or equal to minimum required mongodb version: %s\n", currentMongoVersionSemver, minMongoVersionSemver) return true, nil } err = fmt.Errorf("Current mongodb server version %s is less than minimum required mongodb version: %s", currentMongoVersionSemver, minMongoVersionSemver) @@ -193,7 +193,7 @@ func (qp *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, name return nil, err } - certSecret, err := qp.createPreflightTestSecret(clientset, namespace, certSecretName, certBytes) + certSecret, err := qp.CG.CreatePreflightTestSecret(clientset, namespace, certSecretName, certBytes) if err != nil { err = fmt.Errorf("unable to create secret with cert : %v\n", err) return nil, err @@ -202,6 +202,6 @@ func (qp *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, name } func (qp *QliksensePreflight) runMongoCleanup(clientset *kubernetes.Clientset, namespace, mongoPodName, caCertSecretName string) { - qp.deletePod(clientset, namespace, mongoPodName) - qp.deleteK8sSecret(clientset, namespace, caCertSecretName) + qp.CG.DeletePod(clientset, namespace, mongoPodName) + qp.CG.DeleteK8sSecret(clientset, namespace, caCertSecretName) } diff --git a/pkg/preflight/preflight_utils.go b/pkg/preflight/preflight_utils.go index 3d994223..3c64f7d6 100644 --- a/pkg/preflight/preflight_utils.go +++ b/pkg/preflight/preflight_utils.go @@ -1,29 +1,8 @@ package preflight import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "path/filepath" - "strings" - "time" - - "github.com/mitchellh/go-homedir" "github.com/qlik-oss/sense-installer/pkg/api" "github.com/qlik-oss/sense-installer/pkg/qliksense" - appsv1 "k8s.io/api/apps/v1" - apiv1 "k8s.io/api/core/v1" - "k8s.io/api/rbac/v1beta1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/retry" ) type PreflightOptions struct { @@ -31,709 +10,49 @@ type PreflightOptions struct { MongoOptions *MongoOptions } -// LogVerboseMessage logs a verbose message -func (p *PreflightOptions) LogVerboseMessage(strMessage string, args ...interface{}) { - if p.Verbose || os.Getenv("QLIKSENSE_DEBUG") == "true" { - fmt.Printf(strMessage, args...) - } -} +// // LogVerboseMessage logs a verbose message +// func (p *PreflightOptions) LogVerboseMessage(strMessage string, args ...interface{}) { +// if p.Verbose || os.Getenv("QLIKSENSE_DEBUG") == "true" { +// fmt.Printf(strMessage, args...) +// } +// } type MongoOptions struct { MongodbUrl string CaCertFile string } -var gracePeriod int64 = 0 - type QliksensePreflight struct { - Q *qliksense.Qliksense - P *PreflightOptions + Q *qliksense.Qliksense + P *PreflightOptions + CG *api.ClientGoUtils } func (qp *QliksensePreflight) GetPreflightConfigObj() *api.PreflightConfig { return api.NewPreflightConfig(qp.Q.QliksenseHome) } -func InitPreflight() (string, []byte, error) { - api.LogDebugMessage("Reading .kube/config file...") - - homeDir, err := homedir.Dir() - if err != nil { - err = fmt.Errorf("Unable to deduce home dir\n") - return "", nil, err - } - api.LogDebugMessage("Kube config location: %s\n\n", filepath.Join(homeDir, ".kube", "config")) - - kubeConfig := filepath.Join(homeDir, ".kube", "config") - kubeConfigContents, err := ioutil.ReadFile(kubeConfig) - if err != nil { - err = fmt.Errorf("Unable to deduce home dir\n") - return "", nil, err - } - // retrieve namespace - namespace := api.GetKubectlNamespace() - // if namespace comes back empty, we will run checks in the default namespace - if namespace == "" { - namespace = "default" - } - api.LogDebugMessage("Namespace: %s\n", namespace) - return namespace, kubeConfigContents, nil -} - -func initiateK8sOps(opr, namespace string) error { - opr1 := strings.Fields(opr) - _, err := api.KubectlDirectOps(opr1, namespace) - if err != nil { - fmt.Println(err) - return err - } - return nil -} - -func int32Ptr(i int32) *int32 { return &i } - -func retryOnError(mf func() error) error { - return retry.OnError(wait.Backoff{ - Duration: 1 * time.Second, - Factor: 1, - Jitter: 0.1, - Steps: 5, - }, func(err error) bool { - return k8serrors.IsConflict(err) || k8serrors.IsGone(err) || k8serrors.IsServerTimeout(err) || - k8serrors.IsServiceUnavailable(err) || k8serrors.IsTimeout(err) || k8serrors.IsTooManyRequests(err) - }, mf) -} - -func getK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clientset, *rest.Config, error) { - var clientConfig *rest.Config - var err error - if len(kubeconfig) == 0 { - clientConfig, err = rest.InClusterConfig() - if err != nil { - err = fmt.Errorf("Unable to load in-cluster kubeconfig: %w", err) - return nil, nil, err - } - } else { - config, err := clientcmd.Load(kubeconfig) - if err != nil { - err = fmt.Errorf("Unable to load kubeconfig: %w", err) - return nil, nil, err - } - if contextName != "" { - config.CurrentContext = contextName - } - clientConfig, err = clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig() - if err != nil { - err = fmt.Errorf("Unable to create client config from config: %w", err) - return nil, nil, err - } - } - clientset, err := kubernetes.NewForConfig(clientConfig) - if err != nil { - err = fmt.Errorf("Unable to create clientset: %w", err) - return nil, nil, err - } - return clientset, clientConfig, nil -} - -func (qp *QliksensePreflight) createPreflightTestDeployment(clientset *kubernetes.Clientset, namespace string, depName string, imageName string) (*appsv1.Deployment, error) { - deploymentsClient := clientset.AppsV1().Deployments(namespace) - deployment := &appsv1.Deployment{ - ObjectMeta: v1.ObjectMeta{ - Name: depName, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: int32Ptr(1), - Selector: &v1.LabelSelector{ - MatchLabels: map[string]string{ - "app": "preflight-check", - }, - }, - Template: apiv1.PodTemplateSpec{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "app": "preflight-check", - "label": "preflight-check-label", - }, - }, - Spec: apiv1.PodSpec{ - Containers: []apiv1.Container{ - { - Name: "dep", - Image: imageName, - Ports: []apiv1.ContainerPort{ - { - Name: "http", - Protocol: apiv1.ProtocolTCP, - ContainerPort: 80, - }, - }, - }, - }, - }, - }, - }, - } - - // Create Deployment - var result *appsv1.Deployment - if err := retryOnError(func() (err error) { - result, err = deploymentsClient.Create(deployment) - return err - }); err != nil { - err = fmt.Errorf("unable to create deployments in the %s namespace: %w", namespace, err) - return nil, err - } - qp.P.LogVerboseMessage("Created deployment %q\n", result.GetObjectMeta().GetName()) - - return deployment, nil -} - -func getDeployment(clientset *kubernetes.Clientset, namespace, depName string) (*appsv1.Deployment, error) { - deploymentsClient := clientset.AppsV1().Deployments(namespace) - var deployment *appsv1.Deployment - if err := retryOnError(func() (err error) { - deployment, err = deploymentsClient.Get(depName, v1.GetOptions{}) - return err - }); err != nil { - err = fmt.Errorf("unable to get deployments in the %s namespace: %w", namespace, err) - api.LogDebugMessage("%v\n", err) - return nil, err - } - return deployment, nil -} - -func (qp *QliksensePreflight) deleteDeployment(clientset *kubernetes.Clientset, namespace, name string) error { - deploymentsClient := clientset.AppsV1().Deployments(namespace) - // Create Deployment - deletePolicy := v1.DeletePropagationForeground - deleteOptions := v1.DeleteOptions{ - PropagationPolicy: &deletePolicy, - GracePeriodSeconds: &gracePeriod, - } - - if err := retryOnError(func() (err error) { - return deploymentsClient.Delete(name, &deleteOptions) - }); err != nil { - return err - } - if err := waitForDeploymentToDelete(clientset, namespace, name); err != nil { - return err - } - qp.P.LogVerboseMessage("Deleted deployment: %s\n", name) - return nil -} - -func (qp *QliksensePreflight) createPreflightTestService(clientset *kubernetes.Clientset, namespace string, svcName string) (*apiv1.Service, error) { - iptr := int32Ptr(80) - servicesClient := clientset.CoreV1().Services(namespace) - service := &apiv1.Service{ - ObjectMeta: v1.ObjectMeta{ - Name: svcName, - Namespace: namespace, - Labels: map[string]string{ - "app": "preflight-check", - }, - }, - Spec: apiv1.ServiceSpec{ - Ports: []apiv1.ServicePort{ - {Name: "port1", - Port: *iptr, - }, - }, - Selector: map[string]string{ - "app": "preflight-check", - }, - ClusterIP: "", - }, - } - var result *apiv1.Service - if err := retryOnError(func() (err error) { - result, err = servicesClient.Create(service) - return err - }); err != nil { - return nil, err - } - qp.P.LogVerboseMessage("Created service %q\n", result.GetObjectMeta().GetName()) - - return service, nil -} - -func getService(clientset *kubernetes.Clientset, namespace, svcName string) (*apiv1.Service, error) { - servicesClient := clientset.CoreV1().Services(namespace) - var svc *apiv1.Service - if err := retryOnError(func() (err error) { - svc, err = servicesClient.Get(svcName, v1.GetOptions{}) - return err - }); err != nil { - err = fmt.Errorf("unable to get services in the %s namespace: %w", namespace, err) - return nil, err - } - - return svc, nil -} - -func (qp *QliksensePreflight) deleteService(clientset *kubernetes.Clientset, namespace, name string) error { - servicesClient := clientset.CoreV1().Services(namespace) - // Create Deployment - deletePolicy := v1.DeletePropagationForeground - deleteOptions := v1.DeleteOptions{ - PropagationPolicy: &deletePolicy, - } - if err := retryOnError(func() (err error) { - return servicesClient.Delete(name, &deleteOptions) - }); err != nil { - return err - } - qp.P.LogVerboseMessage("Deleted service: %s\n", name) - return nil -} - -func (qp *QliksensePreflight) deletePod(clientset *kubernetes.Clientset, namespace, name string) error { - - podsClient := clientset.CoreV1().Pods(namespace) - deletePolicy := v1.DeletePropagationForeground - deleteOptions := v1.DeleteOptions{ - PropagationPolicy: &deletePolicy, - GracePeriodSeconds: &gracePeriod, - } - if err := retryOnError(func() (err error) { - return podsClient.Delete(name, &deleteOptions) - }); err != nil { - return err - } - if err := waitForPodToDelete(clientset, namespace, name); err != nil { - return err - } - qp.P.LogVerboseMessage("Deleted pod: %s\n", name) - return nil -} - -func (qp *QliksensePreflight) createPreflightTestPod(clientset *kubernetes.Clientset, namespace, podName, imageName string, secretNames map[string]string, commandToRun []string) (*apiv1.Pod, error) { - // build the pod definition we want to deploy - pod := &apiv1.Pod{ - ObjectMeta: v1.ObjectMeta{ - Name: podName, - Namespace: namespace, - Labels: map[string]string{ - "app": "preflight", - }, - }, - Spec: apiv1.PodSpec{ - RestartPolicy: apiv1.RestartPolicyNever, - Containers: []apiv1.Container{ - { - Name: "cnt", - Image: imageName, - ImagePullPolicy: apiv1.PullIfNotPresent, - Command: commandToRun, - }, - }, - }, - } - if len(secretNames) > 0 { - for secretName, mountPath := range secretNames { - pod.Spec.Volumes = append(pod.Spec.Volumes, apiv1.Volume{ - Name: secretName, - VolumeSource: apiv1.VolumeSource{ - Secret: &apiv1.SecretVolumeSource{ - SecretName: secretName, - Items: []apiv1.KeyToPath{ - { - Key: secretName, - Path: filepath.Base(mountPath), - }, - }, - }, - }, - }) - if len(pod.Spec.Containers) > 0 { - pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, apiv1.VolumeMount{ - Name: secretName, - MountPath: filepath.Dir(mountPath), - ReadOnly: true, - }) - } - } - } - - // now create the pod in kubernetes cluster using the clientset - if err := retryOnError(func() (err error) { - pod, err = clientset.CoreV1().Pods(namespace).Create(pod) - return err - }); err != nil { - return nil, err - } - qp.P.LogVerboseMessage("Created pod: %s\n", pod.Name) - return pod, nil -} - -func getPod(clientset *kubernetes.Clientset, namespace, podName string) (*apiv1.Pod, error) { - api.LogDebugMessage("Fetching pod: %s\n", podName) - var pod *apiv1.Pod - if err := retryOnError(func() (err error) { - pod, err = clientset.CoreV1().Pods(namespace).Get(podName, v1.GetOptions{}) - return err - }); err != nil { - api.LogDebugMessage("%v\n", err) - return nil, err - } - return pod, nil -} - -func getPodLogs(clientset *kubernetes.Clientset, pod *apiv1.Pod) (string, error) { - podLogOpts := apiv1.PodLogOptions{} - - api.LogDebugMessage("Retrieving logs for pod: %s namespace: %s\n", pod.GetName(), pod.Namespace) - req := clientset.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOpts) - podLogs, err := req.Stream() - if err != nil { - return "", err - } - defer podLogs.Close() - time.Sleep(15 * time.Second) - buf := new(bytes.Buffer) - _, err = io.Copy(buf, podLogs) - if err != nil { - return "", err - } - api.LogDebugMessage("Log from pod: %s\n", buf.String()) - return buf.String(), nil -} - -func waitForResource(checkFunc func() (interface{}, error), validateFunc func(interface{}) bool) error { - timeout := time.NewTicker(2 * time.Minute) - defer timeout.Stop() -OUT: - for { - r, err := checkFunc() - if err != nil { - return err - } - select { - case <-timeout.C: - break OUT - default: - if validateFunc(r) { - break OUT - } - } - time.Sleep(5 * time.Second) - } - return nil -} - -func waitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDeployment *appsv1.Deployment) error { - var err error - depName := pfDeployment.GetName() - checkFunc := func() (interface{}, error) { - pfDeployment, err = getDeployment(clientset, namespace, depName) - if err != nil { - err = fmt.Errorf("unable to retrieve deployment: %s\n", depName) - return nil, err - } - return pfDeployment, nil - } - validateFunc := func(data interface{}) bool { - d := data.(*appsv1.Deployment) - return int(d.Status.ReadyReplicas) > 0 - } - if err := waitForResource(checkFunc, validateFunc); err != nil { - return err - } - if int(pfDeployment.Status.ReadyReplicas) == 0 { - err = fmt.Errorf("deployment took longer than expected to spin up pods") - return err - } - return nil -} - -func waitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error { - var err error - if len(pod.Spec.Containers) == 0 { - err = fmt.Errorf("there are no containers in the pod") - return err - } - podName := pod.Name - checkFunc := func() (interface{}, error) { - pod, err = getPod(clientset, namespace, podName) - if err != nil { - err = fmt.Errorf("unable to retrieve %s pod by name", podName) - return nil, err - } - return pod, nil - } - validateFunc := func(data interface{}) bool { - po := data.(*apiv1.Pod) - return po.Status.Phase == apiv1.PodRunning || po.Status.Phase == apiv1.PodSucceeded || po.Status.Phase == apiv1.PodFailed - } - - if err := waitForResource(checkFunc, validateFunc); err != nil { - return err - } - if pod.Status.Phase != apiv1.PodRunning && pod.Status.Phase != apiv1.PodSucceeded && pod.Status.Phase != apiv1.PodFailed { - err = fmt.Errorf("container is taking much longer than expected") - return err - } - return nil -} - -func waitForPodToDie(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error { - podName := pod.Name - checkFunc := func() (interface{}, error) { - po, err := getPod(clientset, namespace, podName) - if err != nil { - err = fmt.Errorf("unable to retrieve %s pod by name", podName) - return nil, err - } - return po, nil - } - validateFunc := func(r interface{}) bool { - po := r.(*apiv1.Pod) - return po.Status.Phase == apiv1.PodFailed || po.Status.Phase == apiv1.PodSucceeded - } - if err := waitForResource(checkFunc, validateFunc); err != nil { - return err - } - return nil -} - -func waitForPodToDelete(clientset *kubernetes.Clientset, namespace, podName string) error { - checkFunc := func() (interface{}, error) { - po, err := getPod(clientset, namespace, podName) - if err != nil { - return nil, err - } - return po, nil - } - validateFunc := func(po interface{}) bool { - return false - } - if err := waitForResource(checkFunc, validateFunc); err != nil { - return nil - } - err := fmt.Errorf("delete pod is taking unusually long") - return err -} - -func waitForDeploymentToDelete(clientset *kubernetes.Clientset, namespace, deploymentName string) error { - checkFunc := func() (interface{}, error) { - dep, err := getDeployment(clientset, namespace, deploymentName) - if err != nil { - return nil, err - } - return dep, nil - } - validateFunc := func(po interface{}) bool { - return false - } - if err := waitForResource(checkFunc, validateFunc); err != nil { - return nil - } - err := fmt.Errorf("delete deployment is taking unusually long") - return err -} - -func (qp *QliksensePreflight) createPfRole(clientset *kubernetes.Clientset, namespace, roleName string) (*v1beta1.Role, error) { - // build the role defination we want to create - var role *v1beta1.Role - roleSpec := &v1beta1.Role{ - ObjectMeta: v1.ObjectMeta{ - Name: roleName, - Namespace: namespace, - Labels: map[string]string{ - "app": "preflight", - }, - }, - Rules: []v1beta1.PolicyRule{}, - } - - // now create the role in kubernetes cluster using the clientset - if err := retryOnError(func() (err error) { - role, err = clientset.RbacV1beta1().Roles(namespace).Create(roleSpec) - return err - }); err != nil { - return nil, err - } - - qp.P.LogVerboseMessage("Created role: %s\n", role.Name) - - return role, nil -} - -func (qp *QliksensePreflight) deleteRole(clientset *kubernetes.Clientset, namespace string, roleName string) error { - rolesClient := clientset.RbacV1beta1().Roles(namespace) - - deletePolicy := v1.DeletePropagationForeground - deleteOptions := v1.DeleteOptions{ - PropagationPolicy: &deletePolicy, - } - err := rolesClient.Delete(roleName, &deleteOptions) - if err != nil { - log.Printf("Error: %v\n", err) - return err - } - qp.P.LogVerboseMessage("Deleted role: %s\n\n", roleName) - return nil -} - -func (qp *QliksensePreflight) createPfRoleBinding(clientset *kubernetes.Clientset, namespace, roleBindingName string) (*v1beta1.RoleBinding, error) { - var roleBinding *v1beta1.RoleBinding - // build the rolebinding defination we want to create - roleBindingSpec := &v1beta1.RoleBinding{ - ObjectMeta: v1.ObjectMeta{ - Name: roleBindingName, - Namespace: namespace, - Labels: map[string]string{ - "app": "preflight", - }, - }, - Subjects: []v1beta1.Subject{ - { - Kind: "ServiceAccount", - APIGroup: "", - Name: "preflight-check-subject", - Namespace: namespace, - }, - }, - RoleRef: v1beta1.RoleRef{ - APIGroup: "", - Kind: "Role", - Name: "preflight-check-roleref", - }, - } - - // now create the roleBinding in kubernetes cluster using the clientset - if err := retryOnError(func() (err error) { - roleBinding, err = clientset.RbacV1beta1().RoleBindings(namespace).Create(roleBindingSpec) - return err - }); err != nil { - return nil, err - } - qp.P.LogVerboseMessage("Created RoleBinding: %s\n", roleBindingSpec.Name) - return roleBinding, nil -} - -func (qp *QliksensePreflight) deleteRoleBinding(clientset *kubernetes.Clientset, namespace string, roleBindingName string) error { - roleBindingClient := clientset.RbacV1beta1().RoleBindings(namespace) - - deletePolicy := v1.DeletePropagationForeground - deleteOptions := v1.DeleteOptions{ - PropagationPolicy: &deletePolicy, - } - err := roleBindingClient.Delete(roleBindingName, &deleteOptions) - if err != nil { - log.Printf("Error: %v\n", err) - return err - } - qp.P.LogVerboseMessage("Deleted RoleBinding: %s\n\n", roleBindingName) - return nil -} - -func (qp *QliksensePreflight) createPfServiceAccount(clientset *kubernetes.Clientset, namespace, serviceAccountName string) (*apiv1.ServiceAccount, error) { - var serviceAccount *apiv1.ServiceAccount - // build the serviceAccount defination we want to create - serviceAccountSpec := &apiv1.ServiceAccount{ - ObjectMeta: v1.ObjectMeta{ - Name: "preflight-check-test-serviceaccount", - Namespace: namespace, - Labels: map[string]string{ - "app": "preflight", - }, - }, - } - - // now create the serviceAccount in kubernetes cluster using the clientset - if err := retryOnError(func() (err error) { - serviceAccount, err = clientset.CoreV1().ServiceAccounts(namespace).Create(serviceAccountSpec) - return err - }); err != nil { - return nil, err - } - qp.P.LogVerboseMessage("Created Service Account: %s\n", serviceAccountSpec.Name) - return serviceAccount, nil -} - -func (qp *QliksensePreflight) deleteServiceAccount(clientset *kubernetes.Clientset, namespace string, serviceAccountName string) error { - serviceAccountClient := clientset.CoreV1().ServiceAccounts(namespace) - - deletePolicy := v1.DeletePropagationForeground - deleteOptions := v1.DeleteOptions{ - PropagationPolicy: &deletePolicy, - } - err := serviceAccountClient.Delete(serviceAccountName, &deleteOptions) - if err != nil { - log.Printf("Error: %v\n", err) - return err - } - qp.P.LogVerboseMessage("Deleted ServiceAccount: %s\n\n", serviceAccountName) - return nil -} - -func (qp *QliksensePreflight) createPreflightTestSecret(clientset *kubernetes.Clientset, namespace, secretName string, secretData []byte) (*apiv1.Secret, error) { - var secret *apiv1.Secret - var err error - // build the secret defination we want to create - secretSpec := &apiv1.Secret{ - ObjectMeta: v1.ObjectMeta{ - Name: secretName, - Namespace: namespace, - Labels: map[string]string{ - "app": "preflight", - }, - }, - Data: map[string][]byte{ - secretName: secretData, - }, - } - - // now create the secret in kubernetes cluster using the clientset - if err = retryOnError(func() (err error) { - secret, err = clientset.CoreV1().Secrets(namespace).Create(secretSpec) - return err - }); err != nil { - return nil, err - } - qp.P.LogVerboseMessage("Created Secret: %s\n", secret.Name) - return secret, nil -} - -func (qp *QliksensePreflight) deleteK8sSecret(clientset *kubernetes.Clientset, namespace string, secretName string) error { - secretClient := clientset.CoreV1().Secrets(namespace) - - deletePolicy := v1.DeletePropagationForeground - deleteOptions := v1.DeleteOptions{ - PropagationPolicy: &deletePolicy, - } - err := secretClient.Delete(secretName, &deleteOptions) - if err != nil { - return err - } - qp.P.LogVerboseMessage("Deleted Secret: %s\n", secretName) - return nil -} - func (qp *QliksensePreflight) Cleanup(namespace string, kubeConfigContents []byte) error { - qp.P.LogVerboseMessage("Preflight clean\n") - qp.P.LogVerboseMessage("----------------\n") + qp.CG.LogVerboseMessage("Preflight clean\n") + qp.CG.LogVerboseMessage("----------------\n") - qp.P.LogVerboseMessage("Removing deployment...\n") + qp.CG.LogVerboseMessage("Removing deployment...\n") qp.CheckDeployment(namespace, kubeConfigContents, true) - qp.P.LogVerboseMessage("Removing service...\n") + qp.CG.LogVerboseMessage("Removing service...\n") qp.CheckService(namespace, kubeConfigContents, true) - qp.P.LogVerboseMessage("Removing pod...\n") + qp.CG.LogVerboseMessage("Removing pod...\n") qp.CheckPod(namespace, kubeConfigContents, true) - qp.P.LogVerboseMessage("Removing role...\n") + qp.CG.LogVerboseMessage("Removing role...\n") qp.CheckCreateRole(namespace, true) - qp.P.LogVerboseMessage("Removing rolebinding...\n") + qp.CG.LogVerboseMessage("Removing rolebinding...\n") qp.CheckCreateRoleBinding(namespace, true) - qp.P.LogVerboseMessage("Removing serviceaccount...\n") + qp.CG.LogVerboseMessage("Removing serviceaccount...\n") qp.CheckCreateServiceAccount(namespace, true) - qp.P.LogVerboseMessage("Removing DNS check components...\n") + qp.CG.LogVerboseMessage("Removing DNS check components...\n") qp.CheckDns(namespace, kubeConfigContents, true) - qp.P.LogVerboseMessage("Removing mongo check components...\n") + qp.CG.LogVerboseMessage("Removing mongo check components...\n") qp.CheckMongo(kubeConfigContents, namespace, &PreflightOptions{MongoOptions: &MongoOptions{}}, true) return nil } diff --git a/pkg/preflight/preflight_utils_test.go b/pkg/preflight/preflight_utils_test.go deleted file mode 100644 index 3998e2d3..00000000 --- a/pkg/preflight/preflight_utils_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package preflight - -import ( - "fmt" - "testing" -) - -func Test_initiateK8sOps(t *testing.T) { - t.Skip() - type args struct { - opr string - namespace string - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "valid case", - args: args{ - opr: fmt.Sprintf("version"), - namespace: "test-ns", - }, - wantErr: false, - }, - { - name: "invalid case", - args: args{ - opr: fmt.Sprintf("versions"), - namespace: "test-ns", - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := initiateK8sOps(tt.args.opr, tt.args.namespace); (err != nil) != tt.wantErr { - t.Errorf("initiateK8sOps() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/pkg/preflight/role_check.go b/pkg/preflight/role_check.go index d5e131ee..3dc90c91 100644 --- a/pkg/preflight/role_check.go +++ b/pkg/preflight/role_check.go @@ -14,15 +14,15 @@ import ( func (qp *QliksensePreflight) CheckCreateRole(namespace string, cleanup bool) error { // create a Role if !cleanup { - qp.P.LogVerboseMessage("Preflight role check: \n") - qp.P.LogVerboseMessage("--------------------- \n") + qp.CG.LogVerboseMessage("Preflight role check: \n") + qp.CG.LogVerboseMessage("--------------------- \n") } err := qp.checkCreateEntity(namespace, "Role", cleanup) if err != nil { return err } if !cleanup { - qp.P.LogVerboseMessage("Completed preflight role check\n") + qp.CG.LogVerboseMessage("Completed preflight role check\n") } return nil } @@ -30,15 +30,15 @@ func (qp *QliksensePreflight) CheckCreateRole(namespace string, cleanup bool) er func (qp *QliksensePreflight) CheckCreateRoleBinding(namespace string, cleanup bool) error { // create a RoleBinding if !cleanup { - qp.P.LogVerboseMessage("Preflight rolebinding check: \n") - qp.P.LogVerboseMessage("---------------------------- \n") + qp.CG.LogVerboseMessage("Preflight rolebinding check: \n") + qp.CG.LogVerboseMessage("---------------------------- \n") } err := qp.checkCreateEntity(namespace, "RoleBinding", cleanup) if err != nil { return err } if !cleanup { - qp.P.LogVerboseMessage("Completed preflight rolebinding check\n") + qp.CG.LogVerboseMessage("Completed preflight rolebinding check\n") } return nil } @@ -46,15 +46,15 @@ func (qp *QliksensePreflight) CheckCreateRoleBinding(namespace string, cleanup b func (qp *QliksensePreflight) CheckCreateServiceAccount(namespace string, cleanup bool) error { // create a service account if !cleanup { - qp.P.LogVerboseMessage("Preflight serviceaccount check: \n") - qp.P.LogVerboseMessage("------------------------------- \n") + qp.CG.LogVerboseMessage("Preflight serviceaccount check: \n") + qp.CG.LogVerboseMessage("------------------------------- \n") } err := qp.checkCreateEntity(namespace, "ServiceAccount", cleanup) if err != nil { return err } if !cleanup { - qp.P.LogVerboseMessage("Completed preflight serviceaccount check\n") + qp.CG.LogVerboseMessage("Completed preflight serviceaccount check\n") } return nil } @@ -67,13 +67,13 @@ func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string, var err error currentCR, err = qConfig.GetCurrentCR() if err != nil { - qp.P.LogVerboseMessage("Unable to retrieve current CR: %v\n", err) + qp.CG.LogVerboseMessage("Unable to retrieve current CR: %v\n", err) return err } if currentCR.IsRepoExist() { mfroot = currentCR.Spec.GetManifestsRoot() } else if tempDownloadedDir, err := qliksense.DownloadFromGitRepoToTmpDir(qliksense.QLIK_GIT_REPO, "master"); err != nil { - qp.P.LogVerboseMessage("Unable to Download from git repo to tmp dir: %v\n", err) + qp.CG.LogVerboseMessage("Unable to Download from git repo to tmp dir: %v\n", err) return err } else { mfroot = tempDownloadedDir @@ -107,10 +107,10 @@ func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string, } defer func() { - qp.P.LogVerboseMessage("Cleaning up resources...\n") + qp.CG.LogVerboseMessage("Cleaning up resources...\n") err := api.KubectlDeleteVerbose(sa, namespace, qp.P.Verbose) if err != nil { - qp.P.LogVerboseMessage("Preflight cleanup failed!\n") + qp.CG.LogVerboseMessage("Preflight cleanup failed!\n") } }() @@ -120,55 +120,55 @@ func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string, return err } - qp.P.LogVerboseMessage("Preflight %s check: PASSED\n", entityToTest) + qp.CG.LogVerboseMessage("Preflight %s check: PASSED\n", entityToTest) return nil } func (qp *QliksensePreflight) CheckCreateRB(namespace string, kubeConfigContents []byte) error { // create a role - qp.P.LogVerboseMessage("Preflight createRole check: \n") - qp.P.LogVerboseMessage("--------------------------- \n") + qp.CG.LogVerboseMessage("Preflight createRole check: \n") + qp.CG.LogVerboseMessage("--------------------------- \n") errStr := strings.Builder{} err1 := qp.checkCreateEntity(namespace, "Role", false) if err1 != nil { errStr.WriteString(err1.Error()) errStr.WriteString("\n") - qp.P.LogVerboseMessage("%v\n", err1) - qp.P.LogVerboseMessage("Preflight role check: FAILED\n") + qp.CG.LogVerboseMessage("%v\n", err1) + qp.CG.LogVerboseMessage("Preflight role check: FAILED\n") } - qp.P.LogVerboseMessage("Completed preflight role check\n\n") + qp.CG.LogVerboseMessage("Completed preflight role check\n\n") // create a roleBinding - qp.P.LogVerboseMessage("Preflight rolebinding check: \n") - qp.P.LogVerboseMessage("---------------------------- \n") + qp.CG.LogVerboseMessage("Preflight rolebinding check: \n") + qp.CG.LogVerboseMessage("---------------------------- \n") err2 := qp.checkCreateEntity(namespace, "RoleBinding", false) if err2 != nil { errStr.WriteString(err2.Error()) errStr.WriteString("\n") - qp.P.LogVerboseMessage("%v\n", err2) - qp.P.LogVerboseMessage("Preflight rolebinding check: FAILED\n") + qp.CG.LogVerboseMessage("%v\n", err2) + qp.CG.LogVerboseMessage("Preflight rolebinding check: FAILED\n") } - qp.P.LogVerboseMessage("Completed preflight rolebinding check\n\n") + qp.CG.LogVerboseMessage("Completed preflight rolebinding check\n\n") // create a service account - qp.P.LogVerboseMessage("Preflight serviceaccount check: \n") - qp.P.LogVerboseMessage("------------------------------- \n") + qp.CG.LogVerboseMessage("Preflight serviceaccount check: \n") + qp.CG.LogVerboseMessage("------------------------------- \n") err3 := qp.checkCreateEntity(namespace, "ServiceAccount", false) if err3 != nil { errStr.WriteString(err3.Error()) errStr.WriteString("\n") - qp.P.LogVerboseMessage("%v\n", err3) - qp.P.LogVerboseMessage("Preflight serviceaccount check: FAILED\n") + qp.CG.LogVerboseMessage("%v\n", err3) + qp.CG.LogVerboseMessage("Preflight serviceaccount check: FAILED\n") } - qp.P.LogVerboseMessage("Completed preflight serviceaccount check\n\n") + qp.CG.LogVerboseMessage("Completed preflight serviceaccount check\n\n") if err1 != nil || err2 != nil || err3 != nil { - qp.P.LogVerboseMessage("Preflight authcheck: FAILED\n") - qp.P.LogVerboseMessage("Completed preflight authcheck\n") + qp.CG.LogVerboseMessage("Preflight authcheck: FAILED\n") + qp.CG.LogVerboseMessage("Completed preflight authcheck\n") return errors.New(errStr.String()) } - qp.P.LogVerboseMessage("Preflight authcheck: PASSED\n") - qp.P.LogVerboseMessage("Completed preflight authcheck\n") + qp.CG.LogVerboseMessage("Preflight authcheck: PASSED\n") + qp.CG.LogVerboseMessage("Completed preflight authcheck\n") return nil } diff --git a/pkg/preflight/version_check.go b/pkg/preflight/version_check.go index 8c87bef4..5ca3fdba 100644 --- a/pkg/preflight/version_check.go +++ b/pkg/preflight/version_check.go @@ -9,24 +9,24 @@ import ( ) func (qp *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigContents []byte) error { - qp.P.LogVerboseMessage("Preflight kubernetes version check: \n") - qp.P.LogVerboseMessage("----------------------------------- \n") + qp.CG.LogVerboseMessage("Preflight kubernetes version check: \n") + qp.CG.LogVerboseMessage("----------------------------------- \n") var currentVersion *semver.Version - clientset, _, err := getK8SClientSet(kubeConfigContents, "") + clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("Unable to create clientset: %v\n", err) return err } var serverVersion *version.Info - if err := retryOnError(func() (err error) { + if err := api.RetryOnError(func() (err error) { serverVersion, err = clientset.ServerVersion() return err }); err != nil { err = fmt.Errorf("Unable to get server version: %v\n", err) return err } - qp.P.LogVerboseMessage("Kubernetes API Server version: %s\n", serverVersion.String()) + qp.CG.LogVerboseMessage("Kubernetes API Server version: %s\n", serverVersion.String()) // Compare K8s version on the cluster with minimum supported k8s version currentVersion, err = semver.NewVersion(serverVersion.String()) @@ -43,7 +43,7 @@ func (qp *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigConten } if currentVersion.GreaterThan(minK8sVersionSemver) { - qp.P.LogVerboseMessage("Current Kubernetes API Server version %s is greater than or equal to minimum required version: %s\n", currentVersion, minK8sVersionSemver) + qp.CG.LogVerboseMessage("Current Kubernetes API Server version %s is greater than or equal to minimum required version: %s\n", currentVersion, minK8sVersionSemver) } else { err = fmt.Errorf("Current Kubernetes API Server version %s is less than minimum required version: %s", currentVersion, minK8sVersionSemver) return err From b17de591f9b433bce7a001d657ff7d2383b02b19 Mon Sep 17 00:00:00 2001 From: Ashwathi Shiva Date: Sun, 31 May 2020 00:08:53 -0400 Subject: [PATCH 3/9] cleanup and adding tests --- cmd/qliksense/postflight.go | 3 +- cmd/qliksense/preflight.go | 24 ++-- pkg/api/clientgo_utils.go | 171 ++++++++++++++------------- pkg/api/clientgo_utils_test.go | 130 ++++++++++++++++++++ pkg/postflight/db_migration_check.go | 17 ++- pkg/preflight/deployability.go | 89 +++++++------- pkg/preflight/dns_check.go | 49 ++++---- pkg/preflight/mongo_check.go | 44 +++---- pkg/preflight/version_check.go | 16 +-- 9 files changed, 342 insertions(+), 201 deletions(-) create mode 100644 pkg/api/clientgo_utils_test.go diff --git a/cmd/qliksense/postflight.go b/cmd/qliksense/postflight.go index 095b55e9..a917c351 100644 --- a/cmd/qliksense/postflight.go +++ b/cmd/qliksense/postflight.go @@ -5,7 +5,6 @@ import ( . "github.com/logrusorgru/aurora" ansi "github.com/mattn/go-colorable" - "github.com/qlik-oss/sense-installer/pkg/api" postflight "github.com/qlik-oss/sense-installer/pkg/postflight" "github.com/qlik-oss/sense-installer/pkg/qliksense" "github.com/spf13/cobra" @@ -36,7 +35,7 @@ func pfMigrationCheck(q *qliksense.Qliksense) *cobra.Command { pf := &postflight.QliksensePostflight{Q: q, P: postflightOpts} // Postflight db_migration_check - namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() + namespace, kubeConfigContents, err := pf.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Postflight db_migration_check FAILED")) fmt.Printf("Error: %v\n", err) diff --git a/cmd/qliksense/preflight.go b/cmd/qliksense/preflight.go index 5ef9c164..4d78fc34 100644 --- a/cmd/qliksense/preflight.go +++ b/cmd/qliksense/preflight.go @@ -41,7 +41,7 @@ func pfDnsCheckCmd(q *qliksense.Qliksense) *cobra.Command { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight DNS check - namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() + namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight DNS check FAILED")) fmt.Printf("Error: %v\n", err) @@ -79,7 +79,7 @@ func pfK8sVersionCheckCmd(q *qliksense.Qliksense) *cobra.Command { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight Kubernetes minimum version check - namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() + namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight kubernetes minimum version check FAILED")) fmt.Printf("Error: %v\n", err) @@ -116,7 +116,7 @@ func pfAllChecksCmd(q *qliksense.Qliksense) *cobra.Command { // Preflight run all checks fmt.Printf("Running all preflight checks...\n\n") - namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() + namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Unable to run the preflight checks suite")) fmt.Printf("Error: %v\n", err) @@ -155,7 +155,7 @@ func pfDeploymentCheckCmd(q *qliksense.Qliksense) *cobra.Command { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight deployments check - namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() + namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED")) fmt.Printf("Error: %v\n", err) @@ -193,7 +193,7 @@ func pfServiceCheckCmd(q *qliksense.Qliksense) *cobra.Command { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight service check - namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() + namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED")) fmt.Printf("Error: %v\n", err) @@ -232,7 +232,7 @@ func pfPodCheckCmd(q *qliksense.Qliksense) *cobra.Command { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight pod check - namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() + namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED")) fmt.Printf("Error: %v\n", err) @@ -270,7 +270,7 @@ func pfCreateRoleCheckCmd(q *qliksense.Qliksense) *cobra.Command { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight role check - namespace, _, err := api.LoadKubeConfigAndNamespace() + namespace, _, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED")) fmt.Printf("Error: %v\n", err) @@ -305,7 +305,7 @@ func pfCreateRoleBindingCheckCmd(q *qliksense.Qliksense) *cobra.Command { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight createRoleBinding check - namespace, _, err := api.LoadKubeConfigAndNamespace() + namespace, _, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight rolebinding check FAILED")) fmt.Printf("Error: %v\n", err) @@ -340,7 +340,7 @@ func pfCreateServiceAccountCheckCmd(q *qliksense.Qliksense) *cobra.Command { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight createServiceAccount check - namespace, _, err := api.LoadKubeConfigAndNamespace() + namespace, _, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight ServiceAccount check FAILED")) fmt.Printf("Error: %v\n", err) @@ -374,7 +374,7 @@ func pfCreateAuthCheckCmd(q *qliksense.Qliksense) *cobra.Command { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight authcheck - namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() + namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight authcheck FAILED")) fmt.Printf("Error: %v\n", err) @@ -409,7 +409,7 @@ func pfMongoCheckCmd(q *qliksense.Qliksense) *cobra.Command { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight mongo check - namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() + namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight mongo check FAILED")) fmt.Printf("Error: %v\n", err) @@ -449,7 +449,7 @@ func pfCleanupCmd(q *qliksense.Qliksense) *cobra.Command { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} // Preflight clean - namespace, kubeConfigContents, err := api.LoadKubeConfigAndNamespace() + namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight cleanup FAILED")) fmt.Printf("Error: %v\n", err) diff --git a/pkg/api/clientgo_utils.go b/pkg/api/clientgo_utils.go index 5b7e2394..d6e4bba9 100644 --- a/pkg/api/clientgo_utils.go +++ b/pkg/api/clientgo_utils.go @@ -38,7 +38,7 @@ func (p *ClientGoUtils) LogVerboseMessage(strMessage string, args ...interface{} func int32Ptr(i int32) *int32 { return &i } -func LoadKubeConfigAndNamespace() (string, []byte, error) { +func (p *ClientGoUtils) LoadKubeConfigAndNamespace() (string, []byte, error) { fmt.Println("Reading .kube/config file...") homeDir, err := homedir.Dir() @@ -65,7 +65,7 @@ func LoadKubeConfigAndNamespace() (string, []byte, error) { return namespace, kubeConfigContents, nil } -func RetryOnError(mf func() error) error { +func (p *ClientGoUtils) RetryOnError(mf func() error) error { return retry.OnError(wait.Backoff{ Duration: 1 * time.Second, Factor: 1, @@ -77,7 +77,7 @@ func RetryOnError(mf func() error) error { }, mf) } -func GetK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clientset, *rest.Config, error) { +func (p *ClientGoUtils) GetK8SClientSet(kubeconfig []byte, contextName string) (kubernetes.Interface, *rest.Config, error) { var clientConfig *rest.Config var err error if len(kubeconfig) == 0 { @@ -109,7 +109,7 @@ func GetK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clients return clientset, clientConfig, nil } -func (qp *ClientGoUtils) CreatePreflightTestDeployment(clientset *kubernetes.Clientset, namespace string, depName string, imageName string) (*appsv1.Deployment, error) { +func (p *ClientGoUtils) CreatePreflightTestDeployment(clientset kubernetes.Interface, namespace string, depName string, imageName string) (*appsv1.Deployment, error) { deploymentsClient := clientset.AppsV1().Deployments(namespace) deployment := &appsv1.Deployment{ ObjectMeta: v1.ObjectMeta{ @@ -150,22 +150,22 @@ func (qp *ClientGoUtils) CreatePreflightTestDeployment(clientset *kubernetes.Cli // Create Deployment var result *appsv1.Deployment - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { result, err = deploymentsClient.Create(deployment) return err }); err != nil { err = fmt.Errorf("unable to create deployments in the %s namespace: %w", namespace, err) return nil, err } - qp.LogVerboseMessage("Created deployment %q\n", result.GetObjectMeta().GetName()) + p.LogVerboseMessage("Created deployment %q\n", result.GetObjectMeta().GetName()) return deployment, nil } -func getDeployment(clientset *kubernetes.Clientset, namespace, depName string) (*appsv1.Deployment, error) { +func (p *ClientGoUtils) getDeployment(clientset kubernetes.Interface, namespace, depName string) (*appsv1.Deployment, error) { deploymentsClient := clientset.AppsV1().Deployments(namespace) var deployment *appsv1.Deployment - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { deployment, err = deploymentsClient.Get(depName, v1.GetOptions{}) return err }); err != nil { @@ -176,7 +176,7 @@ func getDeployment(clientset *kubernetes.Clientset, namespace, depName string) ( return deployment, nil } -func (qp *ClientGoUtils) DeleteDeployment(clientset *kubernetes.Clientset, namespace, name string) error { +func (p *ClientGoUtils) DeleteDeployment(clientset kubernetes.Interface, namespace, name string) error { deploymentsClient := clientset.AppsV1().Deployments(namespace) // Create Deployment deletePolicy := v1.DeletePropagationForeground @@ -185,19 +185,19 @@ func (qp *ClientGoUtils) DeleteDeployment(clientset *kubernetes.Clientset, names GracePeriodSeconds: &gracePeriod, } - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { return deploymentsClient.Delete(name, &deleteOptions) }); err != nil { return err } - if err := WaitForDeploymentToDelete(clientset, namespace, name); err != nil { + if err := p.WaitForDeploymentToDelete(clientset, namespace, name); err != nil { return err } - qp.LogVerboseMessage("Deleted deployment: %s\n", name) + p.LogVerboseMessage("Deleted deployment: %s\n", name) return nil } -func (qp *ClientGoUtils) CreatePreflightTestService(clientset *kubernetes.Clientset, namespace string, svcName string) (*apiv1.Service, error) { +func (p *ClientGoUtils) CreatePreflightTestService(clientset kubernetes.Interface, namespace string, svcName string) (*apiv1.Service, error) { iptr := int32Ptr(80) servicesClient := clientset.CoreV1().Services(namespace) service := &apiv1.Service{ @@ -221,21 +221,21 @@ func (qp *ClientGoUtils) CreatePreflightTestService(clientset *kubernetes.Client }, } var result *apiv1.Service - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { result, err = servicesClient.Create(service) return err }); err != nil { return nil, err } - qp.LogVerboseMessage("Created service %q\n", result.GetObjectMeta().GetName()) + p.LogVerboseMessage("Created service %q\n", result.GetObjectMeta().GetName()) return service, nil } -func GetService(clientset *kubernetes.Clientset, namespace, svcName string) (*apiv1.Service, error) { +func (p *ClientGoUtils) GetService(clientset kubernetes.Interface, namespace, svcName string) (*apiv1.Service, error) { servicesClient := clientset.CoreV1().Services(namespace) var svc *apiv1.Service - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { svc, err = servicesClient.Get(svcName, v1.GetOptions{}) return err }); err != nil { @@ -246,23 +246,23 @@ func GetService(clientset *kubernetes.Clientset, namespace, svcName string) (*ap return svc, nil } -func (qp *ClientGoUtils) DeleteService(clientset *kubernetes.Clientset, namespace, name string) error { +func (p *ClientGoUtils) DeleteService(clientset kubernetes.Interface, namespace, name string) error { servicesClient := clientset.CoreV1().Services(namespace) // Create Deployment deletePolicy := v1.DeletePropagationForeground deleteOptions := v1.DeleteOptions{ PropagationPolicy: &deletePolicy, } - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { return servicesClient.Delete(name, &deleteOptions) }); err != nil { return err } - qp.LogVerboseMessage("Deleted service: %s\n", name) + p.LogVerboseMessage("Deleted service: %s\n", name) return nil } -func (qp *ClientGoUtils) DeletePod(clientset *kubernetes.Clientset, namespace, name string) error { +func (p *ClientGoUtils) DeletePod(clientset kubernetes.Interface, namespace, name string) error { podsClient := clientset.CoreV1().Pods(namespace) deletePolicy := v1.DeletePropagationForeground @@ -270,19 +270,19 @@ func (qp *ClientGoUtils) DeletePod(clientset *kubernetes.Clientset, namespace, n PropagationPolicy: &deletePolicy, GracePeriodSeconds: &gracePeriod, } - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { return podsClient.Delete(name, &deleteOptions) }); err != nil { return err } - if err := waitForPodToDelete(clientset, namespace, name); err != nil { + if err := p.waitForPodToDelete(clientset, namespace, name); err != nil { return err } - qp.LogVerboseMessage("Deleted pod: %s\n", name) + p.LogVerboseMessage("Deleted pod: %s\n", name) return nil } -func (qp *ClientGoUtils) CreatePreflightTestPod(clientset *kubernetes.Clientset, namespace, podName, imageName string, secretNames map[string]string, commandToRun []string) (*apiv1.Pod, error) { +func (p *ClientGoUtils) CreatePreflightTestPod(clientset kubernetes.Interface, namespace, podName, imageName string, secretNames map[string]string, commandToRun []string) (*apiv1.Pod, error) { // build the pod definition we want to deploy pod := &apiv1.Pod{ ObjectMeta: v1.ObjectMeta{ @@ -331,20 +331,20 @@ func (qp *ClientGoUtils) CreatePreflightTestPod(clientset *kubernetes.Clientset, } // now create the pod in kubernetes cluster using the clientset - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { pod, err = clientset.CoreV1().Pods(namespace).Create(pod) return err }); err != nil { return nil, err } - qp.LogVerboseMessage("Created pod: %s\n", pod.Name) + p.LogVerboseMessage("Created pod: %s\n", pod.Name) return pod, nil } -func getPod(clientset *kubernetes.Clientset, namespace, podName string) (*apiv1.Pod, error) { +func (p *ClientGoUtils) getPod(clientset kubernetes.Interface, namespace, podName string) (*apiv1.Pod, error) { LogDebugMessage("Fetching pod: %s\n", podName) var pod *apiv1.Pod - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { pod, err = clientset.CoreV1().Pods(namespace).Get(podName, v1.GetOptions{}) return err }); err != nil { @@ -354,8 +354,15 @@ func getPod(clientset *kubernetes.Clientset, namespace, podName string) (*apiv1. return pod, nil } -func GetPodLogs(clientset *kubernetes.Clientset, pod *apiv1.Pod) (string, error) { +func (p *ClientGoUtils) GetPodLogs(clientset kubernetes.Interface, pod *apiv1.Pod) (string, error) { + return p.GetPodContainerLogs(clientset, pod, "") +} + +func (p *ClientGoUtils) GetPodContainerLogs(clientset kubernetes.Interface, pod *apiv1.Pod, container string) (string, error) { podLogOpts := apiv1.PodLogOptions{} + if container != "" { + podLogOpts.Container = container + } LogDebugMessage("Retrieving logs for pod: %s namespace: %s\n", pod.GetName(), pod.Namespace) req := clientset.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOpts) @@ -374,7 +381,7 @@ func GetPodLogs(clientset *kubernetes.Clientset, pod *apiv1.Pod) (string, error) return buf.String(), nil } -func waitForResource(checkFunc func() (interface{}, error), validateFunc func(interface{}) bool) error { +func (p *ClientGoUtils) waitForResource(checkFunc func() (interface{}, error), validateFunc func(interface{}) bool) error { timeout := time.NewTicker(2 * time.Minute) defer timeout.Stop() OUT: @@ -396,11 +403,11 @@ OUT: return nil } -func WaitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDeployment *appsv1.Deployment) error { +func (p *ClientGoUtils) WaitForDeployment(clientset kubernetes.Interface, namespace string, pfDeployment *appsv1.Deployment) error { var err error depName := pfDeployment.GetName() checkFunc := func() (interface{}, error) { - pfDeployment, err = getDeployment(clientset, namespace, depName) + pfDeployment, err = p.getDeployment(clientset, namespace, depName) if err != nil { err = fmt.Errorf("unable to retrieve deployment: %s\n", depName) return nil, err @@ -411,7 +418,7 @@ func WaitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDepl d := data.(*appsv1.Deployment) return int(d.Status.ReadyReplicas) > 0 } - if err := waitForResource(checkFunc, validateFunc); err != nil { + if err := p.waitForResource(checkFunc, validateFunc); err != nil { return err } if int(pfDeployment.Status.ReadyReplicas) == 0 { @@ -421,7 +428,7 @@ func WaitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDepl return nil } -func WaitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error { +func (p *ClientGoUtils) WaitForPod(clientset kubernetes.Interface, namespace string, pod *apiv1.Pod) error { var err error if len(pod.Spec.Containers) == 0 { err = fmt.Errorf("there are no containers in the pod") @@ -429,7 +436,7 @@ func WaitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Po } podName := pod.Name checkFunc := func() (interface{}, error) { - pod, err = getPod(clientset, namespace, podName) + pod, err = p.getPod(clientset, namespace, podName) if err != nil { err = fmt.Errorf("unable to retrieve %s pod by name", podName) return nil, err @@ -441,7 +448,7 @@ func WaitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Po return po.Status.Phase == apiv1.PodRunning || po.Status.Phase == apiv1.PodSucceeded || po.Status.Phase == apiv1.PodFailed } - if err := waitForResource(checkFunc, validateFunc); err != nil { + if err := p.waitForResource(checkFunc, validateFunc); err != nil { return err } if pod.Status.Phase != apiv1.PodRunning && pod.Status.Phase != apiv1.PodSucceeded && pod.Status.Phase != apiv1.PodFailed { @@ -451,10 +458,10 @@ func WaitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Po return nil } -func WaitForPodToDie(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error { +func (p *ClientGoUtils) WaitForPodToDie(clientset kubernetes.Interface, namespace string, pod *apiv1.Pod) error { podName := pod.Name checkFunc := func() (interface{}, error) { - po, err := getPod(clientset, namespace, podName) + po, err := p.getPod(clientset, namespace, podName) if err != nil { err = fmt.Errorf("unable to retrieve %s pod by name", podName) return nil, err @@ -465,15 +472,15 @@ func WaitForPodToDie(clientset *kubernetes.Clientset, namespace string, pod *api po := r.(*apiv1.Pod) return po.Status.Phase == apiv1.PodFailed || po.Status.Phase == apiv1.PodSucceeded } - if err := waitForResource(checkFunc, validateFunc); err != nil { + if err := p.waitForResource(checkFunc, validateFunc); err != nil { return err } return nil } -func waitForPodToDelete(clientset *kubernetes.Clientset, namespace, podName string) error { +func (p *ClientGoUtils) waitForPodToDelete(clientset kubernetes.Interface, namespace, podName string) error { checkFunc := func() (interface{}, error) { - po, err := getPod(clientset, namespace, podName) + po, err := p.getPod(clientset, namespace, podName) if err != nil { return nil, err } @@ -482,16 +489,16 @@ func waitForPodToDelete(clientset *kubernetes.Clientset, namespace, podName stri validateFunc := func(po interface{}) bool { return false } - if err := waitForResource(checkFunc, validateFunc); err != nil { + if err := p.waitForResource(checkFunc, validateFunc); err != nil { return nil } err := fmt.Errorf("delete pod is taking unusually long") return err } -func WaitForDeploymentToDelete(clientset *kubernetes.Clientset, namespace, deploymentName string) error { +func (p *ClientGoUtils) WaitForDeploymentToDelete(clientset kubernetes.Interface, namespace, deploymentName string) error { checkFunc := func() (interface{}, error) { - dep, err := getDeployment(clientset, namespace, deploymentName) + dep, err := p.getDeployment(clientset, namespace, deploymentName) if err != nil { return nil, err } @@ -500,14 +507,14 @@ func WaitForDeploymentToDelete(clientset *kubernetes.Clientset, namespace, deplo validateFunc := func(po interface{}) bool { return false } - if err := waitForResource(checkFunc, validateFunc); err != nil { + if err := p.waitForResource(checkFunc, validateFunc); err != nil { return nil } err := fmt.Errorf("delete deployment is taking unusually long") return err } -func (qp *ClientGoUtils) CreatePfRole(clientset *kubernetes.Clientset, namespace, roleName string) (*v1beta1.Role, error) { +func (p *ClientGoUtils) CreatePfRole(clientset kubernetes.Interface, namespace, roleName string) (*v1beta1.Role, error) { // build the role defination we want to create var role *v1beta1.Role roleSpec := &v1beta1.Role{ @@ -522,19 +529,19 @@ func (qp *ClientGoUtils) CreatePfRole(clientset *kubernetes.Clientset, namespace } // now create the role in kubernetes cluster using the clientset - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { role, err = clientset.RbacV1beta1().Roles(namespace).Create(roleSpec) return err }); err != nil { return nil, err } - qp.LogVerboseMessage("Created role: %s\n", role.Name) + p.LogVerboseMessage("Created role: %s\n", role.Name) return role, nil } -func (qp *ClientGoUtils) DeleteRole(clientset *kubernetes.Clientset, namespace string, roleName string) error { +func (p *ClientGoUtils) DeleteRole(clientset kubernetes.Interface, namespace string, roleName string) error { rolesClient := clientset.RbacV1beta1().Roles(namespace) deletePolicy := v1.DeletePropagationForeground @@ -546,11 +553,11 @@ func (qp *ClientGoUtils) DeleteRole(clientset *kubernetes.Clientset, namespace s log.Printf("Error: %v\n", err) return err } - qp.LogVerboseMessage("Deleted role: %s\n\n", roleName) + p.LogVerboseMessage("Deleted role: %s\n\n", roleName) return nil } -func (qp *ClientGoUtils) CreatePfRoleBinding(clientset *kubernetes.Clientset, namespace, roleBindingName string) (*v1beta1.RoleBinding, error) { +func (p *ClientGoUtils) CreatePfRoleBinding(clientset kubernetes.Interface, namespace, roleBindingName string) (*v1beta1.RoleBinding, error) { var roleBinding *v1beta1.RoleBinding // build the rolebinding defination we want to create roleBindingSpec := &v1beta1.RoleBinding{ @@ -577,17 +584,17 @@ func (qp *ClientGoUtils) CreatePfRoleBinding(clientset *kubernetes.Clientset, na } // now create the roleBinding in kubernetes cluster using the clientset - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { roleBinding, err = clientset.RbacV1beta1().RoleBindings(namespace).Create(roleBindingSpec) return err }); err != nil { return nil, err } - qp.LogVerboseMessage("Created RoleBinding: %s\n", roleBindingSpec.Name) + p.LogVerboseMessage("Created RoleBinding: %s\n", roleBindingSpec.Name) return roleBinding, nil } -func (qp *ClientGoUtils) DeleteRoleBinding(clientset *kubernetes.Clientset, namespace string, roleBindingName string) error { +func (p *ClientGoUtils) DeleteRoleBinding(clientset kubernetes.Interface, namespace string, roleBindingName string) error { roleBindingClient := clientset.RbacV1beta1().RoleBindings(namespace) deletePolicy := v1.DeletePropagationForeground @@ -599,11 +606,11 @@ func (qp *ClientGoUtils) DeleteRoleBinding(clientset *kubernetes.Clientset, name log.Printf("Error: %v\n", err) return err } - qp.LogVerboseMessage("Deleted RoleBinding: %s\n\n", roleBindingName) + p.LogVerboseMessage("Deleted RoleBinding: %s\n\n", roleBindingName) return nil } -func (qp *ClientGoUtils) CreatePfServiceAccount(clientset *kubernetes.Clientset, namespace, serviceAccountName string) (*apiv1.ServiceAccount, error) { +func (p *ClientGoUtils) CreatePfServiceAccount(clientset kubernetes.Interface, namespace, serviceAccountName string) (*apiv1.ServiceAccount, error) { var serviceAccount *apiv1.ServiceAccount // build the serviceAccount defination we want to create serviceAccountSpec := &apiv1.ServiceAccount{ @@ -617,17 +624,17 @@ func (qp *ClientGoUtils) CreatePfServiceAccount(clientset *kubernetes.Clientset, } // now create the serviceAccount in kubernetes cluster using the clientset - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { serviceAccount, err = clientset.CoreV1().ServiceAccounts(namespace).Create(serviceAccountSpec) return err }); err != nil { return nil, err } - qp.LogVerboseMessage("Created Service Account: %s\n", serviceAccountSpec.Name) + p.LogVerboseMessage("Created Service Account: %s\n", serviceAccountSpec.Name) return serviceAccount, nil } -func (qp *ClientGoUtils) DeleteServiceAccount(clientset *kubernetes.Clientset, namespace string, serviceAccountName string) error { +func (p *ClientGoUtils) DeleteServiceAccount(clientset kubernetes.Interface, namespace string, serviceAccountName string) error { serviceAccountClient := clientset.CoreV1().ServiceAccounts(namespace) deletePolicy := v1.DeletePropagationForeground @@ -639,11 +646,11 @@ func (qp *ClientGoUtils) DeleteServiceAccount(clientset *kubernetes.Clientset, n log.Printf("Error: %v\n", err) return err } - qp.LogVerboseMessage("Deleted ServiceAccount: %s\n\n", serviceAccountName) + p.LogVerboseMessage("Deleted ServiceAccount: %s\n\n", serviceAccountName) return nil } -func (qp *ClientGoUtils) CreatePreflightTestSecret(clientset *kubernetes.Clientset, namespace, secretName string, secretData []byte) (*apiv1.Secret, error) { +func (p *ClientGoUtils) CreatePreflightTestSecret(clientset kubernetes.Interface, namespace, secretName string, secretData []byte) (*apiv1.Secret, error) { var secret *apiv1.Secret var err error // build the secret defination we want to create @@ -661,17 +668,17 @@ func (qp *ClientGoUtils) CreatePreflightTestSecret(clientset *kubernetes.Clients } // now create the secret in kubernetes cluster using the clientset - if err = RetryOnError(func() (err error) { + if err = p.RetryOnError(func() (err error) { secret, err = clientset.CoreV1().Secrets(namespace).Create(secretSpec) return err }); err != nil { return nil, err } - qp.LogVerboseMessage("Created Secret: %s\n", secret.Name) + p.LogVerboseMessage("Created Secret: %s\n", secret.Name) return secret, nil } -func (qp *ClientGoUtils) DeleteK8sSecret(clientset *kubernetes.Clientset, namespace string, secretName string) error { +func (p *ClientGoUtils) DeleteK8sSecret(clientset kubernetes.Interface, namespace string, secretName string) error { secretClient := clientset.CoreV1().Secrets(namespace) deletePolicy := v1.DeletePropagationForeground @@ -682,11 +689,11 @@ func (qp *ClientGoUtils) DeleteK8sSecret(clientset *kubernetes.Clientset, namesp if err != nil { return err } - qp.LogVerboseMessage("Deleted Secret: %s\n", secretName) + p.LogVerboseMessage("Deleted Secret: %s\n", secretName) return nil } -func CreateStatefulSet(clientset *kubernetes.Clientset, namespace string, statefulSetName string, imageName string) (*appsv1.StatefulSet, error) { +func (p *ClientGoUtils) CreateStatefulSet(clientset kubernetes.Interface, namespace string, statefulSetName string, imageName string) (*appsv1.StatefulSet, error) { statefulSetsClient := clientset.AppsV1().StatefulSets(namespace) statefulset := &appsv1.StatefulSet{ ObjectMeta: v1.ObjectMeta{ @@ -736,7 +743,7 @@ func CreateStatefulSet(clientset *kubernetes.Clientset, namespace string, statef // Create Deployment var result *appsv1.StatefulSet - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { result, err = statefulSetsClient.Create(statefulset) return err }); err != nil { @@ -748,7 +755,7 @@ func CreateStatefulSet(clientset *kubernetes.Clientset, namespace string, statef return statefulset, nil } -func GetPodsForStatefulset(clientset *kubernetes.Clientset, statefulset *appsv1.StatefulSet, namespace string) (*apiv1.PodList, error) { +func (p *ClientGoUtils) GetPodsForStatefulset(clientset kubernetes.Interface, statefulset *appsv1.StatefulSet, namespace string) (*apiv1.PodList, error) { set := labels.Set(statefulset.Spec.Template.Labels) listOptions := v1.ListOptions{LabelSelector: set.AsSelector().String()} pods, err := clientset.CoreV1().Pods(namespace).List(listOptions) @@ -758,11 +765,11 @@ func GetPodsForStatefulset(clientset *kubernetes.Clientset, statefulset *appsv1. return pods, err } -func waitForStatefulSet(clientset *kubernetes.Clientset, namespace string, pfStatefulset *appsv1.StatefulSet) error { +func (p *ClientGoUtils) waitForStatefulSet(clientset kubernetes.Interface, namespace string, pfStatefulset *appsv1.StatefulSet) error { var err error statefulsetName := pfStatefulset.GetName() checkFunc := func() (interface{}, error) { - pfStatefulset, err = getStatefulset(clientset, namespace, statefulsetName) + pfStatefulset, err = p.getStatefulset(clientset, namespace, statefulsetName) if err != nil { err = fmt.Errorf("unable to retrieve stateful set: %s\n", statefulsetName) return nil, err @@ -773,7 +780,7 @@ func waitForStatefulSet(clientset *kubernetes.Clientset, namespace string, pfSta s := data.(*appsv1.StatefulSet) return int(s.Status.ReadyReplicas) > 0 } - if err := waitForResource(checkFunc, validateFunc); err != nil { + if err := p.waitForResource(checkFunc, validateFunc); err != nil { return err } if int(pfStatefulset.Status.ReadyReplicas) == 0 { @@ -783,10 +790,10 @@ func waitForStatefulSet(clientset *kubernetes.Clientset, namespace string, pfSta return nil } -func getStatefulset(clientset *kubernetes.Clientset, namespace, statefulsetName string) (*appsv1.StatefulSet, error) { +func (p *ClientGoUtils) getStatefulset(clientset kubernetes.Interface, namespace, statefulsetName string) (*appsv1.StatefulSet, error) { statefulsetsClient := clientset.AppsV1().StatefulSets(namespace) var statefulset *appsv1.StatefulSet - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { statefulset, err = statefulsetsClient.Get(statefulsetName, v1.GetOptions{}) return err }); err != nil { @@ -797,7 +804,7 @@ func getStatefulset(clientset *kubernetes.Clientset, namespace, statefulsetName return statefulset, nil } -func deleteStatefulSet(clientset *kubernetes.Clientset, namespace, name string) error { +func (p *ClientGoUtils) deleteStatefulSet(clientset kubernetes.Interface, namespace, name string) error { statefulsetClient := clientset.AppsV1().StatefulSets(namespace) deletePolicy := v1.DeletePropagationForeground @@ -806,21 +813,21 @@ func deleteStatefulSet(clientset *kubernetes.Clientset, namespace, name string) GracePeriodSeconds: &gracePeriod, } - if err := RetryOnError(func() (err error) { + if err := p.RetryOnError(func() (err error) { return statefulsetClient.Delete(name, &deleteOptions) }); err != nil { return err } - if err := waitForStatefulsetToDelete(clientset, namespace, name); err != nil { + if err := p.waitForStatefulsetToDelete(clientset, namespace, name); err != nil { return err } fmt.Printf("Deleted statefulset: %s\n", name) return nil } -func waitForStatefulsetToDelete(clientset *kubernetes.Clientset, namespace, statefulsetName string) error { +func (p *ClientGoUtils) waitForStatefulsetToDelete(clientset kubernetes.Interface, namespace, statefulsetName string) error { checkFunc := func() (interface{}, error) { - statefulset, err := getStatefulset(clientset, namespace, statefulsetName) + statefulset, err := p.getStatefulset(clientset, namespace, statefulsetName) if err != nil { return nil, err } @@ -829,14 +836,14 @@ func waitForStatefulsetToDelete(clientset *kubernetes.Clientset, namespace, stat validateFunc := func(po interface{}) bool { return false } - if err := waitForResource(checkFunc, validateFunc); err != nil { + if err := p.waitForResource(checkFunc, validateFunc); err != nil { return nil } err := fmt.Errorf("delete statefulset is taking unusually long") return err } -func GetPodsAndPodLogsForFailedInitContainer(clientset *kubernetes.Clientset, lbls map[string]string, namespace, containerName string) error { +func (p *ClientGoUtils) GetPodsAndPodLogsForFailedInitContainer(clientset kubernetes.Interface, lbls map[string]string, namespace, containerName string) error { set := labels.Set(lbls) listOptions := v1.ListOptions{LabelSelector: set.AsSelector().String()} podList, err := clientset.CoreV1().Pods(namespace).List(listOptions) @@ -852,7 +859,7 @@ func GetPodsAndPodLogsForFailedInitContainer(clientset *kubernetes.Clientset, lb for _, cs := range pod.Status.InitContainerStatuses { if (cs.State.Terminated != nil && (cs.State.Terminated.Reason != "Completed" || cs.State.Terminated.ExitCode > 0)) || (cs.LastTerminationState.Terminated != nil && (cs.LastTerminationState.Terminated.Reason != "Completed" || cs.LastTerminationState.Terminated.ExitCode > 0)) && (cs.Name == containerName) { - logs, err := GetPodLogs(clientset, &pod) + logs, err := p.GetPodContainerLogs(clientset, &pod, cs.Name) if err != nil { err = fmt.Errorf("unable to get pod logs: %v", err) fmt.Printf("%s\n", err) diff --git a/pkg/api/clientgo_utils_test.go b/pkg/api/clientgo_utils_test.go new file mode 100644 index 00000000..fb9a6985 --- /dev/null +++ b/pkg/api/clientgo_utils_test.go @@ -0,0 +1,130 @@ +package api + +import ( + "reflect" + "testing" + + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/fake" +) + +func TestClientGoUtils_getDeployment(t *testing.T) { + type fields struct { + Verbose bool + } + type args struct { + clientset kubernetes.Interface + namespace string + depName string + } + tests := []struct { + name string + fields fields + args args + want *appsv1.Deployment + wantErr bool + }{ + { + name: "valid case", + fields: fields{Verbose: true}, + args: args{ + clientset: fake.NewSimpleClientset(&appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dep", + Namespace: "test-ns", + }, + }), + namespace: "test-ns", + depName: "test-dep", + }, + want: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dep", + Namespace: "test-ns", + }, + }, + wantErr: false, + }, + { + name: "error case", + fields: fields{Verbose: true}, + args: args{ + clientset: fake.NewSimpleClientset(), + namespace: "test-ns", + depName: "test-dep", + }, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &ClientGoUtils{ + Verbose: tt.fields.Verbose, + } + got, err := p.getDeployment(tt.args.clientset, tt.args.namespace, tt.args.depName) + if (err != nil) != tt.wantErr { + t.Errorf("ClientGoUtils.getDeployment() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ClientGoUtils.getDeployment() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestClientGoUtils_DeleteDeployment(t *testing.T) { + type fields struct { + Verbose bool + } + type args struct { + clientset kubernetes.Interface + namespace string + name string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "valid case", + fields: fields{Verbose: true}, + args: args{ + clientset: fake.NewSimpleClientset(&appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dep", + Namespace: "test-ns", + }, + }), + namespace: "test-ns", + name: "test-dep", + }, + wantErr: false, + }, + { + name: "valid case", + fields: fields{Verbose: true}, + args: args{ + clientset: fake.NewSimpleClientset(), + namespace: "test-ns", + name: "test-dep", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &ClientGoUtils{ + Verbose: tt.fields.Verbose, + } + if err := p.DeleteDeployment(tt.args.clientset, tt.args.namespace, tt.args.name); (err != nil) != tt.wantErr { + t.Errorf("ClientGoUtils.DeleteDeployment() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/pkg/postflight/db_migration_check.go b/pkg/postflight/db_migration_check.go index 38beea04..466d51c7 100644 --- a/pkg/postflight/db_migration_check.go +++ b/pkg/postflight/db_migration_check.go @@ -3,15 +3,14 @@ package postflight import ( "fmt" - "github.com/qlik-oss/sense-installer/pkg/api" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const initContainerNameToCheck = "migration" -func (qp *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigContents []byte) error { +func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigContents []byte) error { - clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") + clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("unable to create a kubernetes client: %v", err) fmt.Printf("%s\n", err) @@ -24,7 +23,11 @@ func (qp *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigCont fmt.Printf("Number of deployments found: %d\n", deployments.Size()) for _, deployment := range deployments.Items { fmt.Printf("Deployment name: %s\n", deployment.GetName()) - err = api.GetPodsAndPodLogsForFailedInitContainer(clientset, deployment.Spec.Template.Labels, namespace, initContainerNameToCheck) + if err = p.CG.GetPodsAndPodLogsForFailedInitContainer(clientset, deployment.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { + // err = fmt.Errorf("unable to fetch d a kubernetes client: %v", err) + fmt.Printf("%s\n", err) + return err + } } // retrieve all statefulsets @@ -33,7 +36,11 @@ func (qp *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigCont fmt.Printf("Number of statefulsets found: %d\n", statefulsets.Size()) for _, statefulset := range statefulsets.Items { fmt.Printf("Deployment name: %s\n", statefulset.GetName()) - err = api.GetPodsAndPodLogsForFailedInitContainer(clientset, statefulset.Spec.Template.Labels, namespace, initContainerNameToCheck) + if err = p.CG.GetPodsAndPodLogsForFailedInitContainer(clientset, statefulset.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { + // err = fmt.Errorf("unable to create a kubernetes client: %v", err) + fmt.Printf("%s\n", err) + return err + } } fmt.Printf("all done!\n") diff --git a/pkg/preflight/deployability.go b/pkg/preflight/deployability.go index 27709b53..08ce83d8 100644 --- a/pkg/preflight/deployability.go +++ b/pkg/preflight/deployability.go @@ -3,12 +3,11 @@ package preflight import ( "fmt" - "github.com/qlik-oss/sense-installer/pkg/api" "k8s.io/client-go/kubernetes" ) -func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigContents []byte, cleanup bool) error { - clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") +func (p *QliksensePreflight) CheckDeployment(namespace string, kubeConfigContents []byte, cleanup bool) error { + clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("Kube config error: %v\n", err) return err @@ -16,142 +15,142 @@ func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigConten // Deployment check if !cleanup { - qp.CG.LogVerboseMessage("Preflight deployment check: \n") - qp.CG.LogVerboseMessage("--------------------------- \n") + p.CG.LogVerboseMessage("Preflight deployment check: \n") + p.CG.LogVerboseMessage("--------------------------- \n") } - err = qp.checkPfDeployment(clientset, namespace, cleanup) + err = p.checkPfDeployment(clientset, namespace, cleanup) if err != nil { - qp.CG.LogVerboseMessage("Preflight Deployment check: FAILED\n") + p.CG.LogVerboseMessage("Preflight Deployment check: FAILED\n") return err } if !cleanup { - qp.CG.LogVerboseMessage("Completed preflight deployment check\n") + p.CG.LogVerboseMessage("Completed preflight deployment check\n") } return nil } -func (qp *QliksensePreflight) CheckService(namespace string, kubeConfigContents []byte, cleanup bool) error { - clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") +func (p *QliksensePreflight) CheckService(namespace string, kubeConfigContents []byte, cleanup bool) error { + clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("unable to create a kubernetes client: %v\n", err) return err } // Service check if !cleanup { - qp.CG.LogVerboseMessage("Preflight service check: \n") - qp.CG.LogVerboseMessage("------------------------ \n") + p.CG.LogVerboseMessage("Preflight service check: \n") + p.CG.LogVerboseMessage("------------------------ \n") } - err = qp.checkPfService(clientset, namespace, cleanup) + err = p.checkPfService(clientset, namespace, cleanup) if err != nil { - qp.CG.LogVerboseMessage("Preflight Service check: FAILED\n") + p.CG.LogVerboseMessage("Preflight Service check: FAILED\n") return err } if !cleanup { - qp.CG.LogVerboseMessage("Completed preflight service check\n") + p.CG.LogVerboseMessage("Completed preflight service check\n") } return nil } -func (qp *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []byte, cleanup bool) error { - clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") +func (p *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []byte, cleanup bool) error { + clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err) return err } // Pod check if !cleanup { - qp.CG.LogVerboseMessage("Preflight pod check: \n") - qp.CG.LogVerboseMessage("-------------------- \n") + p.CG.LogVerboseMessage("Preflight pod check: \n") + p.CG.LogVerboseMessage("-------------------- \n") } - err = qp.checkPfPod(clientset, namespace, cleanup) + err = p.checkPfPod(clientset, namespace, cleanup) if err != nil { - qp.CG.LogVerboseMessage("Preflight Pod check: FAILED\n") + p.CG.LogVerboseMessage("Preflight Pod check: FAILED\n") return err } if !cleanup { - qp.CG.LogVerboseMessage("Completed preflight pod check\n") + p.CG.LogVerboseMessage("Completed preflight pod check\n") } return nil } -func (qp *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { +func (p *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { // delete the pod we are going to create, if it already exists in the cluster podName := "pod-pf-check" - qp.CG.DeletePod(clientset, namespace, podName) + p.CG.DeletePod(clientset, namespace, podName) if cleanup { return nil } commandToRun := []string{} - imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true) + imageName, err := p.GetPreflightConfigObj().GetImageName(nginx, true) if err != nil { return err } // create a pod - pod, err := qp.CG.CreatePreflightTestPod(clientset, namespace, podName, imageName, nil, commandToRun) + pod, err := p.CG.CreatePreflightTestPod(clientset, namespace, podName, imageName, nil, commandToRun) if err != nil { err = fmt.Errorf("unable to create pod - %v\n", err) return err } - defer qp.CG.DeletePod(clientset, namespace, podName) + defer p.CG.DeletePod(clientset, namespace, podName) - if err := api.WaitForPod(clientset, namespace, pod); err != nil { + if err := p.CG.WaitForPod(clientset, namespace, pod); err != nil { return err } - qp.CG.LogVerboseMessage("Preflight pod creation check: PASSED\n") - qp.CG.LogVerboseMessage("Cleaning up resources...\n") + p.CG.LogVerboseMessage("Preflight pod creation check: PASSED\n") + p.CG.LogVerboseMessage("Cleaning up resources...\n") return nil } -func (qp *QliksensePreflight) checkPfService(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { +func (p *QliksensePreflight) checkPfService(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { // delete the service we are going to create, if it already exists in the cluster serviceName := "svc-pf-check" - qp.CG.DeleteService(clientset, namespace, serviceName) + p.CG.DeleteService(clientset, namespace, serviceName) if cleanup { return nil } // creating service - pfService, err := qp.CG.CreatePreflightTestService(clientset, namespace, serviceName) + pfService, err := p.CG.CreatePreflightTestService(clientset, namespace, serviceName) if err != nil { err = fmt.Errorf("unable to create service - %v\n", err) return err } - defer qp.CG.DeleteService(clientset, namespace, serviceName) - _, err = api.GetService(clientset, namespace, pfService.GetName()) + defer p.CG.DeleteService(clientset, namespace, serviceName) + _, err = p.CG.GetService(clientset, namespace, pfService.GetName()) if err != nil { err = fmt.Errorf("unable to retrieve service - %v\n", err) return err } - qp.CG.LogVerboseMessage("Preflight service creation check: PASSED\n") - qp.CG.LogVerboseMessage("Cleaning up resources...\n") + p.CG.LogVerboseMessage("Preflight service creation check: PASSED\n") + p.CG.LogVerboseMessage("Cleaning up resources...\n") return nil } -func (qp *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { +func (p *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { // delete the deployment we are going to create, if it already exists in the cluster depName := "deployment-preflight-check" - qp.CG.DeleteDeployment(clientset, namespace, depName) + p.CG.DeleteDeployment(clientset, namespace, depName) if cleanup { return nil } // check if we are able to create a deployment - imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true) + imageName, err := p.GetPreflightConfigObj().GetImageName(nginx, true) if err != nil { return err } - pfDeployment, err := qp.CG.CreatePreflightTestDeployment(clientset, namespace, depName, imageName) + pfDeployment, err := p.CG.CreatePreflightTestDeployment(clientset, namespace, depName, imageName) if err != nil { err = fmt.Errorf("unable to create deployment - %v\n", err) return err } - defer qp.CG.DeleteDeployment(clientset, namespace, depName) - if err := api.WaitForDeployment(clientset, namespace, pfDeployment); err != nil { + defer p.CG.DeleteDeployment(clientset, namespace, depName) + if err := p.CG.WaitForDeployment(clientset, namespace, pfDeployment); err != nil { return err } - qp.CG.LogVerboseMessage("Preflight Deployment check: PASSED\n") - qp.CG.LogVerboseMessage("Cleaning up resources...\n") + p.CG.LogVerboseMessage("Preflight Deployment check: PASSED\n") + p.CG.LogVerboseMessage("Cleaning up resources...\n") return nil } diff --git a/pkg/preflight/dns_check.go b/pkg/preflight/dns_check.go index 9afcea26..a6ffbb38 100644 --- a/pkg/preflight/dns_check.go +++ b/pkg/preflight/dns_check.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/qlik-oss/sense-installer/pkg/api" "k8s.io/client-go/kubernetes" ) @@ -13,68 +12,68 @@ const ( netcat = "netcat" ) -func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byte, cleanup bool) error { +func (p *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byte, cleanup bool) error { depName := "dep-dns-preflight-check" serviceName := "svc-dns-pf-check" podName := "pf-pod-1" if !cleanup { - qp.CG.LogVerboseMessage("Preflight DNS check: \n") - qp.CG.LogVerboseMessage("------------------- \n") + p.CG.LogVerboseMessage("Preflight DNS check: \n") + p.CG.LogVerboseMessage("------------------- \n") } - clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") + clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("unable to create a kubernetes client: %v\n", err) return err } // delete the deployment we are going to create, if it already exists in the cluster - qp.runDNSCleanup(clientset, namespace, podName, serviceName, depName) + p.runDNSCleanup(clientset, namespace, podName, serviceName, depName) if cleanup { return nil } // creating deployment - nginxImageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true) + nginxImageName, err := p.GetPreflightConfigObj().GetImageName(nginx, true) if err != nil { return err } - dnsDeployment, err := qp.CG.CreatePreflightTestDeployment(clientset, namespace, depName, nginxImageName) + dnsDeployment, err := p.CG.CreatePreflightTestDeployment(clientset, namespace, depName, nginxImageName) if err != nil { err = fmt.Errorf("unable to create deployment: %v\n", err) return err } - defer qp.CG.DeleteDeployment(clientset, namespace, depName) + defer p.CG.DeleteDeployment(clientset, namespace, depName) - if err := api.WaitForDeployment(clientset, namespace, dnsDeployment); err != nil { + if err := p.CG.WaitForDeployment(clientset, namespace, dnsDeployment); err != nil { return err } // creating service - dnsService, err := qp.CG.CreatePreflightTestService(clientset, namespace, serviceName) + dnsService, err := p.CG.CreatePreflightTestService(clientset, namespace, serviceName) if err != nil { err = fmt.Errorf("unable to create service : %s, %s\n", serviceName, err) return err } - defer qp.CG.DeleteService(clientset, namespace, serviceName) + defer p.CG.DeleteService(clientset, namespace, serviceName) // create a pod commandToRun := []string{"sh", "-c", "sleep 10; nc -z -v -w 1 " + dnsService.Name + " 80"} - netcatImageName, err := qp.GetPreflightConfigObj().GetImageName(netcat, true) + netcatImageName, err := p.GetPreflightConfigObj().GetImageName(netcat, true) if err != nil { err = fmt.Errorf("unable to retrieve image : %v\n", err) return err } - dnsPod, err := qp.CG.CreatePreflightTestPod(clientset, namespace, podName, netcatImageName, nil, commandToRun) + dnsPod, err := p.CG.CreatePreflightTestPod(clientset, namespace, podName, netcatImageName, nil, commandToRun) if err != nil { err = fmt.Errorf("unable to create pod : %s, %s\n", podName, err) return err } - defer qp.CG.DeletePod(clientset, namespace, podName) + defer p.CG.DeletePod(clientset, namespace, podName) - if err := api.WaitForPod(clientset, namespace, dnsPod); err != nil { + if err := p.CG.WaitForPod(clientset, namespace, dnsPod); err != nil { return err } if len(dnsPod.Spec.Containers) == 0 { @@ -82,30 +81,30 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by return err } - api.WaitForPodToDie(clientset, namespace, dnsPod) + p.CG.WaitForPodToDie(clientset, namespace, dnsPod) - logStr, err := api.GetPodLogs(clientset, dnsPod) + logStr, err := p.CG.GetPodLogs(clientset, dnsPod) if err != nil { err = fmt.Errorf("unable to execute dns check in the cluster: %v", err) return err } if strings.HasSuffix(strings.TrimSpace(logStr), "succeeded!") { - qp.CG.LogVerboseMessage("Preflight DNS check: PASSED\n") + p.CG.LogVerboseMessage("Preflight DNS check: PASSED\n") } else { err = fmt.Errorf("Expected response not found\n") return err } if !cleanup { - qp.CG.LogVerboseMessage("Completed preflight DNS check\n") - qp.CG.LogVerboseMessage("Cleaning up resources...\n") + p.CG.LogVerboseMessage("Completed preflight DNS check\n") + p.CG.LogVerboseMessage("Cleaning up resources...\n") } return nil } -func (qp *QliksensePreflight) runDNSCleanup(clientset *kubernetes.Clientset, namespace, podName, serviceName, depName string) { - qp.CG.DeleteDeployment(clientset, namespace, depName) - qp.CG.DeletePod(clientset, namespace, podName) - qp.CG.DeleteService(clientset, namespace, serviceName) +func (p *QliksensePreflight) runDNSCleanup(clientset *kubernetes.Clientset, namespace, podName, serviceName, depName string) { + p.CG.DeleteDeployment(clientset, namespace, depName) + p.CG.DeletePod(clientset, namespace, podName) + p.CG.DeleteService(clientset, namespace, serviceName) } diff --git a/pkg/preflight/mongo_check.go b/pkg/preflight/mongo_check.go index 817056de..46c054ff 100644 --- a/pkg/preflight/mongo_check.go +++ b/pkg/preflight/mongo_check.go @@ -76,29 +76,29 @@ func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace st return nil } -func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error { +func (p *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error { caCertSecretName := "ca-certificates-crt" mongoPodName := "pf-mongo-pod" - clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") + clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("unable to create a kubernetes client: %v\n", err) return err } // cleanup before starting check - qp.runMongoCleanup(clientset, namespace, mongoPodName, caCertSecretName) + p.runMongoCleanup(clientset, namespace, mongoPodName, caCertSecretName) if cleanup { return nil } secrets := map[string]string{} if preflightOpts.MongoOptions.CaCertFile != "" { - caCertSecret, err := qp.createSecret(clientset, namespace, preflightOpts.MongoOptions.CaCertFile, caCertSecretName) + caCertSecret, err := p.createSecret(clientset, namespace, preflightOpts.MongoOptions.CaCertFile, caCertSecretName) if err != nil { err = fmt.Errorf("unable to create a ca cert kubernetes secret: %v\n", err) return err } - defer qp.CG.DeleteK8sSecret(clientset, namespace, caCertSecret.Name) + defer p.CG.DeleteK8sSecret(clientset, namespace, caCertSecret.Name) secrets[caCertSecretName] = caCertMountPath } @@ -106,35 +106,35 @@ func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespac api.LogDebugMessage("Mongo command: %s\n", strings.Join(commandToRun, " ")) // create a pod - imageName, err := qp.GetPreflightConfigObj().GetImageName(preflight_mongo, true) + imageName, err := p.GetPreflightConfigObj().GetImageName(preflight_mongo, true) if err != nil { err = fmt.Errorf("unable to retrieve image : %v\n", err) return err } api.LogDebugMessage("image name to be used: %s\n", imageName) - mongoPod, err := qp.CG.CreatePreflightTestPod(clientset, namespace, mongoPodName, imageName, secrets, commandToRun) + mongoPod, err := p.CG.CreatePreflightTestPod(clientset, namespace, mongoPodName, imageName, secrets, commandToRun) if err != nil { err = fmt.Errorf("unable to create pod : %v\n", err) return err } - defer qp.CG.DeletePod(clientset, namespace, mongoPodName) + defer p.CG.DeletePod(clientset, namespace, mongoPodName) - if err := api.WaitForPod(clientset, namespace, mongoPod); err != nil { + if err := p.CG.WaitForPod(clientset, namespace, mongoPod); err != nil { return err } if len(mongoPod.Spec.Containers) == 0 { err := fmt.Errorf("there are no containers in the pod- %v\n", err) return err } - api.WaitForPodToDie(clientset, namespace, mongoPod) - logStr, err := api.GetPodLogs(clientset, mongoPod) + p.CG.WaitForPodToDie(clientset, namespace, mongoPod) + logStr, err := p.CG.GetPodLogs(clientset, mongoPod) if err != nil { err = fmt.Errorf("unable to execute mongo check in the cluster: %v\n", err) return err } // check mongo server version - ok, err := qp.checkMongoVersion(logStr) + ok, err := p.checkMongoVersion(logStr) if !ok || err != nil { return err } @@ -142,7 +142,7 @@ func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespac // check if connection succeeded stringToCheck := "qlik - connection succeeded!!" if strings.Contains(logStr, stringToCheck) { - qp.CG.LogVerboseMessage("Preflight mongo check: PASSED\n") + p.CG.LogVerboseMessage("Preflight mongo check: PASSED\n") } else { err = fmt.Errorf("Connection failed: %s\n", logStr) return err @@ -150,9 +150,9 @@ func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespac return nil } -func (qp *QliksensePreflight) checkMongoVersion(logStr string) (bool, error) { +func (p *QliksensePreflight) checkMongoVersion(logStr string) (bool, error) { // check mongo server version - api.LogDebugMessage("Minimum required mongo version: %s\n", qp.GetPreflightConfigObj().GetMinMongoVersion()) + api.LogDebugMessage("Minimum required mongo version: %s\n", p.GetPreflightConfigObj().GetMinMongoVersion()) mongoVersionStrToCheck := "qlik mongo server version:" if strings.Contains(logStr, mongoVersionStrToCheck) { logLines := strings.Split(logStr, "\n") @@ -169,13 +169,13 @@ func (qp *QliksensePreflight) checkMongoVersion(logStr string) (bool, error) { err = fmt.Errorf("Unable to convert minimum mongo version into semver version:%v\n", err) return false, err } - minMongoVersionSemver, err := semver.NewVersion(qp.GetPreflightConfigObj().GetMinMongoVersion()) + minMongoVersionSemver, err := semver.NewVersion(p.GetPreflightConfigObj().GetMinMongoVersion()) if err != nil { err = fmt.Errorf("Unable to convert required minimum mongo version into semver version:%v\n", err) return false, err } if currentMongoVersionSemver.GreaterThan(minMongoVersionSemver) || currentMongoVersionSemver.Equal(minMongoVersionSemver) { - qp.CG.LogVerboseMessage("Current mongodb server version %s is greater than or equal to minimum required mongodb version: %s\n", currentMongoVersionSemver, minMongoVersionSemver) + p.CG.LogVerboseMessage("Current mongodb server version %s is greater than or equal to minimum required mongodb version: %s\n", currentMongoVersionSemver, minMongoVersionSemver) return true, nil } err = fmt.Errorf("Current mongodb server version %s is less than minimum required mongodb version: %s", currentMongoVersionSemver, minMongoVersionSemver) @@ -187,13 +187,13 @@ func (qp *QliksensePreflight) checkMongoVersion(logStr string) (bool, error) { return false, err } -func (qp *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, namespace, certFile, certSecretName string) (*apiv1.Secret, error) { +func (p *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, namespace, certFile, certSecretName string) (*apiv1.Secret, error) { certBytes, err := ioutil.ReadFile(certFile) if err != nil { return nil, err } - certSecret, err := qp.CG.CreatePreflightTestSecret(clientset, namespace, certSecretName, certBytes) + certSecret, err := p.CG.CreatePreflightTestSecret(clientset, namespace, certSecretName, certBytes) if err != nil { err = fmt.Errorf("unable to create secret with cert : %v\n", err) return nil, err @@ -201,7 +201,7 @@ func (qp *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, name return certSecret, nil } -func (qp *QliksensePreflight) runMongoCleanup(clientset *kubernetes.Clientset, namespace, mongoPodName, caCertSecretName string) { - qp.CG.DeletePod(clientset, namespace, mongoPodName) - qp.CG.DeleteK8sSecret(clientset, namespace, caCertSecretName) +func (p *QliksensePreflight) runMongoCleanup(clientset *kubernetes.Clientset, namespace, mongoPodName, caCertSecretName string) { + p.CG.DeletePod(clientset, namespace, mongoPodName) + p.CG.DeleteK8sSecret(clientset, namespace, caCertSecretName) } diff --git a/pkg/preflight/version_check.go b/pkg/preflight/version_check.go index 5ca3fdba..c71a14b4 100644 --- a/pkg/preflight/version_check.go +++ b/pkg/preflight/version_check.go @@ -8,25 +8,25 @@ import ( "k8s.io/apimachinery/pkg/version" ) -func (qp *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigContents []byte) error { - qp.CG.LogVerboseMessage("Preflight kubernetes version check: \n") - qp.CG.LogVerboseMessage("----------------------------------- \n") +func (p *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigContents []byte) error { + p.CG.LogVerboseMessage("Preflight kubernetes version check: \n") + p.CG.LogVerboseMessage("----------------------------------- \n") var currentVersion *semver.Version - clientset, _, err := api.GetK8SClientSet(kubeConfigContents, "") + clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "") if err != nil { err = fmt.Errorf("Unable to create clientset: %v\n", err) return err } var serverVersion *version.Info - if err := api.RetryOnError(func() (err error) { + if err := p.CG.RetryOnError(func() (err error) { serverVersion, err = clientset.ServerVersion() return err }); err != nil { err = fmt.Errorf("Unable to get server version: %v\n", err) return err } - qp.CG.LogVerboseMessage("Kubernetes API Server version: %s\n", serverVersion.String()) + p.CG.LogVerboseMessage("Kubernetes API Server version: %s\n", serverVersion.String()) // Compare K8s version on the cluster with minimum supported k8s version currentVersion, err = semver.NewVersion(serverVersion.String()) @@ -36,14 +36,14 @@ func (qp *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigConten } api.LogDebugMessage("Current Kubernetes Version: %v\n", currentVersion) - minK8sVersionSemver, err := semver.NewVersion(qp.GetPreflightConfigObj().GetMinK8sVersion()) + minK8sVersionSemver, err := semver.NewVersion(p.GetPreflightConfigObj().GetMinK8sVersion()) if err != nil { err = fmt.Errorf("Unable to convert minimum Kubernetes version into semver version:%v\n", err) return err } if currentVersion.GreaterThan(minK8sVersionSemver) { - qp.CG.LogVerboseMessage("Current Kubernetes API Server version %s is greater than or equal to minimum required version: %s\n", currentVersion, minK8sVersionSemver) + p.CG.LogVerboseMessage("Current Kubernetes API Server version %s is greater than or equal to minimum required version: %s\n", currentVersion, minK8sVersionSemver) } else { err = fmt.Errorf("Current Kubernetes API Server version %s is less than minimum required version: %s", currentVersion, minK8sVersionSemver) return err From 8281fb962e3f5da06ae3878ac18d9d77a15953ce Mon Sep 17 00:00:00 2001 From: Ashwathi Shiva Date: Sun, 31 May 2020 20:19:11 -0400 Subject: [PATCH 4/9] filtered errors from logs --- cmd/qliksense/postflight.go | 2 +- pkg/api/clientgo_utils.go | 34 ++++++++++---------- pkg/api/clientgo_utils_test.go | 47 ++++++++++++++++++++++++++++ pkg/postflight/db_migration_check.go | 34 ++++++++++++++------ pkg/preflight/deployability.go | 6 ++-- pkg/preflight/dns_check.go | 2 +- pkg/preflight/mongo_check.go | 4 +-- 7 files changed, 96 insertions(+), 33 deletions(-) diff --git a/cmd/qliksense/postflight.go b/cmd/qliksense/postflight.go index a917c351..060773c4 100644 --- a/cmd/qliksense/postflight.go +++ b/cmd/qliksense/postflight.go @@ -49,7 +49,7 @@ func pfMigrationCheck(q *qliksense.Qliksense) *cobra.Command { fmt.Printf("Error: %v\n", err) return nil } - fmt.Fprintf(out, "%s\n", Green("Postflight db_migration_check PASSED")) + fmt.Fprintf(out, "%s\n", Green("Postflight db_migration_check completed")) return nil }, } diff --git a/pkg/api/clientgo_utils.go b/pkg/api/clientgo_utils.go index d6e4bba9..a32f0e5d 100644 --- a/pkg/api/clientgo_utils.go +++ b/pkg/api/clientgo_utils.go @@ -39,14 +39,14 @@ func (p *ClientGoUtils) LogVerboseMessage(strMessage string, args ...interface{} func int32Ptr(i int32) *int32 { return &i } func (p *ClientGoUtils) LoadKubeConfigAndNamespace() (string, []byte, error) { - fmt.Println("Reading .kube/config file...") + LogDebugMessage("Reading .kube/config file...") homeDir, err := homedir.Dir() if err != nil { err = fmt.Errorf("Unable to deduce home dir") return "", nil, err } - fmt.Printf("Kube config location: %s\n\n", filepath.Join(homeDir, ".kube", "config")) + LogDebugMessage("Kube config location: %s\n\n", filepath.Join(homeDir, ".kube", "config")) kubeConfig := filepath.Join(homeDir, ".kube", "config") kubeConfigContents, err := ioutil.ReadFile(kubeConfig) @@ -77,7 +77,7 @@ func (p *ClientGoUtils) RetryOnError(mf func() error) error { }, mf) } -func (p *ClientGoUtils) GetK8SClientSet(kubeconfig []byte, contextName string) (kubernetes.Interface, *rest.Config, error) { +func (p *ClientGoUtils) GetK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clientset, *rest.Config, error) { var clientConfig *rest.Config var err error if len(kubeconfig) == 0 { @@ -741,7 +741,7 @@ func (p *ClientGoUtils) CreateStatefulSet(clientset kubernetes.Interface, namesp }, } - // Create Deployment + // Create Statefulset var result *appsv1.StatefulSet if err := p.RetryOnError(func() (err error) { result, err = statefulSetsClient.Create(statefulset) @@ -750,7 +750,7 @@ func (p *ClientGoUtils) CreateStatefulSet(clientset kubernetes.Interface, namesp err = fmt.Errorf("unable to create statefulsets in the %s namespace: %w", namespace, err) return nil, err } - fmt.Printf("Created statefulset %q\n", result.GetObjectMeta().GetName()) + LogDebugMessage("Created statefulset %q\n", result.GetObjectMeta().GetName()) return statefulset, nil } @@ -760,7 +760,7 @@ func (p *ClientGoUtils) GetPodsForStatefulset(clientset kubernetes.Interface, st listOptions := v1.ListOptions{LabelSelector: set.AsSelector().String()} pods, err := clientset.CoreV1().Pods(namespace).List(listOptions) for _, pod := range pods.Items { - fmt.Fprintf(os.Stdout, "*********** pod name: %v\n", pod.Name) + LogDebugMessage("pod: %v\n", pod.Name) } return pods, err } @@ -821,7 +821,7 @@ func (p *ClientGoUtils) deleteStatefulSet(clientset kubernetes.Interface, namesp if err := p.waitForStatefulsetToDelete(clientset, namespace, name); err != nil { return err } - fmt.Printf("Deleted statefulset: %s\n", name) + LogDebugMessage("Deleted statefulset: %s\n", name) return nil } @@ -843,7 +843,7 @@ func (p *ClientGoUtils) waitForStatefulsetToDelete(clientset kubernetes.Interfac return err } -func (p *ClientGoUtils) GetPodsAndPodLogsForFailedInitContainer(clientset kubernetes.Interface, lbls map[string]string, namespace, containerName string) error { +func (p *ClientGoUtils) GetPodsAndPodLogsFromFailedInitContainer(clientset kubernetes.Interface, lbls map[string]string, namespace, containerName string) (string, error) { set := labels.Set(lbls) listOptions := v1.ListOptions{LabelSelector: set.AsSelector().String()} podList, err := clientset.CoreV1().Pods(namespace).List(listOptions) @@ -851,23 +851,23 @@ func (p *ClientGoUtils) GetPodsAndPodLogsForFailedInitContainer(clientset kubern err = fmt.Errorf("unable to get podlist: %v", err) fmt.Printf("%s\n", err) } - fmt.Printf("%d Pods retrieved\n ", len(podList.Items)) + LogDebugMessage("%d Pods retrieved\n ", len(podList.Items)) + var logs string for _, pod := range podList.Items { - fmt.Fprintf(os.Stdout, "*********** pod name: %v\n", pod.Name) - fmt.Printf("%d init containers retrieved\n", len(pod.Spec.InitContainers)) + LogDebugMessage("pod: %v\n", pod.Name) + LogDebugMessage("%d init containers retrieved\n", len(pod.Spec.InitContainers)) for _, cs := range pod.Status.InitContainerStatuses { - if (cs.State.Terminated != nil && (cs.State.Terminated.Reason != "Completed" || cs.State.Terminated.ExitCode > 0)) || - (cs.LastTerminationState.Terminated != nil && (cs.LastTerminationState.Terminated.Reason != "Completed" || cs.LastTerminationState.Terminated.ExitCode > 0)) && (cs.Name == containerName) { - logs, err := p.GetPodContainerLogs(clientset, &pod, cs.Name) + if cs.Name == containerName && ((cs.State.Terminated != nil && (cs.State.Terminated.Reason != "Completed" || cs.State.Terminated.ExitCode > 0)) || + (cs.LastTerminationState.Terminated != nil && (cs.LastTerminationState.Terminated.Reason != "Completed" || cs.LastTerminationState.Terminated.ExitCode > 0))) { + logs, err = p.GetPodContainerLogs(clientset, &pod, cs.Name) if err != nil { err = fmt.Errorf("unable to get pod logs: %v", err) fmt.Printf("%s\n", err) - return err + return "", err } - fmt.Printf("Retrieved logs from init container: %s\n%s", cs.Name, logs) } } } - return nil + return logs, nil } diff --git a/pkg/api/clientgo_utils_test.go b/pkg/api/clientgo_utils_test.go index fb9a6985..6d5e6ac6 100644 --- a/pkg/api/clientgo_utils_test.go +++ b/pkg/api/clientgo_utils_test.go @@ -5,6 +5,7 @@ import ( "testing" appsv1 "k8s.io/api/apps/v1" + apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" @@ -128,3 +129,49 @@ func TestClientGoUtils_DeleteDeployment(t *testing.T) { }) } } + +func TestClientGoUtils_GetService(t *testing.T) { + type fields struct { + Verbose bool + } + type args struct { + clientset kubernetes.Interface + namespace string + svcName string + } + tests := []struct { + name string + fields fields + args args + want *apiv1.Service + wantErr bool + }{ + { + name: "valid case", + fields: fields{Verbose: true}, + args: args{}, + want: &appsv1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-svc", + Namespace: "test-ns", + }, + wantErr: false, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &ClientGoUtils{ + Verbose: tt.fields.Verbose, + } + got, err := p.GetService(tt.args.clientset, tt.args.namespace, tt.args.svcName) + if (err != nil) != tt.wantErr { + t.Errorf("ClientGoUtils.GetService() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ClientGoUtils.GetService() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/postflight/db_migration_check.go b/pkg/postflight/db_migration_check.go index 466d51c7..f6583926 100644 --- a/pkg/postflight/db_migration_check.go +++ b/pkg/postflight/db_migration_check.go @@ -2,7 +2,9 @@ package postflight import ( "fmt" + "strings" + "github.com/qlik-oss/sense-installer/pkg/api" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -17,32 +19,46 @@ func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigConte return err } + var logs string // Retrieve all deployments deploymentsClient := clientset.AppsV1().Deployments(namespace) deployments, err := deploymentsClient.List(v1.ListOptions{}) - fmt.Printf("Number of deployments found: %d\n", deployments.Size()) + api.LogDebugMessage("Number of deployments found: %d\n", deployments.Size()) for _, deployment := range deployments.Items { - fmt.Printf("Deployment name: %s\n", deployment.GetName()) - if err = p.CG.GetPodsAndPodLogsForFailedInitContainer(clientset, deployment.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { - // err = fmt.Errorf("unable to fetch d a kubernetes client: %v", err) + api.LogDebugMessage("Deployment name: %s\n", deployment.GetName()) + if logs, err = p.CG.GetPodsAndPodLogsFromFailedInitContainer(clientset, deployment.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { fmt.Printf("%s\n", err) return err } + p.filterLogsForErrors(logs) } // retrieve all statefulsets statefulsetsClient := clientset.AppsV1().StatefulSets(namespace) statefulsets, err := statefulsetsClient.List(v1.ListOptions{}) - fmt.Printf("Number of statefulsets found: %d\n", statefulsets.Size()) + api.LogDebugMessage("Number of statefulsets found: %d\n", statefulsets.Size()) for _, statefulset := range statefulsets.Items { - fmt.Printf("Deployment name: %s\n", statefulset.GetName()) - if err = p.CG.GetPodsAndPodLogsForFailedInitContainer(clientset, statefulset.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { - // err = fmt.Errorf("unable to create a kubernetes client: %v", err) + api.LogDebugMessage("Statefulset name: %s\n", statefulset.GetName()) + if logs, err = p.CG.GetPodsAndPodLogsFromFailedInitContainer(clientset, statefulset.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { fmt.Printf("%s\n", err) return err } + p.filterLogsForErrors(logs) } - fmt.Printf("all done!\n") return nil } + +func (p *QliksensePostflight) filterLogsForErrors(logs string) { + containerLogs := strings.Split(logs, "\n") + if len(containerLogs) > 0 { + for _, logLine := range containerLogs { + api.LogDebugMessage("init container logs: \n") + if strings.Contains(strings.ToLower(logLine), "error") { + fmt.Printf("%s\n", logLine) + } + } + } else { + p.CG.LogVerboseMessage("no logs obtained\n") + } +} diff --git a/pkg/preflight/deployability.go b/pkg/preflight/deployability.go index 08ce83d8..c35e5807 100644 --- a/pkg/preflight/deployability.go +++ b/pkg/preflight/deployability.go @@ -75,7 +75,7 @@ func (p *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []byt return nil } -func (p *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { +func (p *QliksensePreflight) checkPfPod(clientset kubernetes.Interface, namespace string, cleanup bool) error { // delete the pod we are going to create, if it already exists in the cluster podName := "pod-pf-check" p.CG.DeletePod(clientset, namespace, podName) @@ -104,7 +104,7 @@ func (p *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namespa return nil } -func (p *QliksensePreflight) checkPfService(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { +func (p *QliksensePreflight) checkPfService(clientset kubernetes.Interface, namespace string, cleanup bool) error { // delete the service we are going to create, if it already exists in the cluster serviceName := "svc-pf-check" p.CG.DeleteService(clientset, namespace, serviceName) @@ -128,7 +128,7 @@ func (p *QliksensePreflight) checkPfService(clientset *kubernetes.Clientset, nam return nil } -func (p *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset, namespace string, cleanup bool) error { +func (p *QliksensePreflight) checkPfDeployment(clientset kubernetes.Interface, namespace string, cleanup bool) error { // delete the deployment we are going to create, if it already exists in the cluster depName := "deployment-preflight-check" p.CG.DeleteDeployment(clientset, namespace, depName) diff --git a/pkg/preflight/dns_check.go b/pkg/preflight/dns_check.go index a6ffbb38..e7ee14bf 100644 --- a/pkg/preflight/dns_check.go +++ b/pkg/preflight/dns_check.go @@ -103,7 +103,7 @@ func (p *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byt return nil } -func (p *QliksensePreflight) runDNSCleanup(clientset *kubernetes.Clientset, namespace, podName, serviceName, depName string) { +func (p *QliksensePreflight) runDNSCleanup(clientset kubernetes.Interface, namespace, podName, serviceName, depName string) { p.CG.DeleteDeployment(clientset, namespace, depName) p.CG.DeletePod(clientset, namespace, podName) p.CG.DeleteService(clientset, namespace, serviceName) diff --git a/pkg/preflight/mongo_check.go b/pkg/preflight/mongo_check.go index 46c054ff..6981828b 100644 --- a/pkg/preflight/mongo_check.go +++ b/pkg/preflight/mongo_check.go @@ -187,7 +187,7 @@ func (p *QliksensePreflight) checkMongoVersion(logStr string) (bool, error) { return false, err } -func (p *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, namespace, certFile, certSecretName string) (*apiv1.Secret, error) { +func (p *QliksensePreflight) createSecret(clientset kubernetes.Interface, namespace, certFile, certSecretName string) (*apiv1.Secret, error) { certBytes, err := ioutil.ReadFile(certFile) if err != nil { return nil, err @@ -201,7 +201,7 @@ func (p *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, names return certSecret, nil } -func (p *QliksensePreflight) runMongoCleanup(clientset *kubernetes.Clientset, namespace, mongoPodName, caCertSecretName string) { +func (p *QliksensePreflight) runMongoCleanup(clientset kubernetes.Interface, namespace, mongoPodName, caCertSecretName string) { p.CG.DeletePod(clientset, namespace, mongoPodName) p.CG.DeleteK8sSecret(clientset, namespace, caCertSecretName) } From b26b063eba1a984c3cb530a3ab9d8f11dd949883 Mon Sep 17 00:00:00 2001 From: Ashwathi Shiva Date: Sun, 31 May 2020 20:49:23 -0400 Subject: [PATCH 5/9] Include pod name with error log --- pkg/api/clientgo_utils.go | 11 +++++----- pkg/postflight/db_migration_check.go | 31 +++++++++++++++------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/pkg/api/clientgo_utils.go b/pkg/api/clientgo_utils.go index a32f0e5d..7452db93 100644 --- a/pkg/api/clientgo_utils.go +++ b/pkg/api/clientgo_utils.go @@ -843,7 +843,7 @@ func (p *ClientGoUtils) waitForStatefulsetToDelete(clientset kubernetes.Interfac return err } -func (p *ClientGoUtils) GetPodsAndPodLogsFromFailedInitContainer(clientset kubernetes.Interface, lbls map[string]string, namespace, containerName string) (string, error) { +func (p *ClientGoUtils) GetPodsAndPodLogsFromFailedInitContainer(clientset kubernetes.Interface, lbls map[string]string, namespace, containerName string) (map[string]string, error) { set := labels.Set(lbls) listOptions := v1.ListOptions{LabelSelector: set.AsSelector().String()} podList, err := clientset.CoreV1().Pods(namespace).List(listOptions) @@ -853,18 +853,19 @@ func (p *ClientGoUtils) GetPodsAndPodLogsFromFailedInitContainer(clientset kuber } LogDebugMessage("%d Pods retrieved\n ", len(podList.Items)) - var logs string + // var logs map[string]string + logs := map[string]string{} for _, pod := range podList.Items { - LogDebugMessage("pod: %v\n", pod.Name) + LogDebugMessage("pod: %v\n", pod.GetName()) LogDebugMessage("%d init containers retrieved\n", len(pod.Spec.InitContainers)) for _, cs := range pod.Status.InitContainerStatuses { if cs.Name == containerName && ((cs.State.Terminated != nil && (cs.State.Terminated.Reason != "Completed" || cs.State.Terminated.ExitCode > 0)) || (cs.LastTerminationState.Terminated != nil && (cs.LastTerminationState.Terminated.Reason != "Completed" || cs.LastTerminationState.Terminated.ExitCode > 0))) { - logs, err = p.GetPodContainerLogs(clientset, &pod, cs.Name) + logs[pod.GetName()], err = p.GetPodContainerLogs(clientset, &pod, cs.Name) if err != nil { err = fmt.Errorf("unable to get pod logs: %v", err) fmt.Printf("%s\n", err) - return "", err + return nil, err } } } diff --git a/pkg/postflight/db_migration_check.go b/pkg/postflight/db_migration_check.go index f6583926..5ae53685 100644 --- a/pkg/postflight/db_migration_check.go +++ b/pkg/postflight/db_migration_check.go @@ -19,18 +19,19 @@ func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigConte return err } - var logs string + var logsMap map[string]string + // Retrieve all deployments deploymentsClient := clientset.AppsV1().Deployments(namespace) deployments, err := deploymentsClient.List(v1.ListOptions{}) api.LogDebugMessage("Number of deployments found: %d\n", deployments.Size()) for _, deployment := range deployments.Items { api.LogDebugMessage("Deployment name: %s\n", deployment.GetName()) - if logs, err = p.CG.GetPodsAndPodLogsFromFailedInitContainer(clientset, deployment.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { + if logsMap, err = p.CG.GetPodsAndPodLogsFromFailedInitContainer(clientset, deployment.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { fmt.Printf("%s\n", err) return err } - p.filterLogsForErrors(logs) + p.filterLogsForErrors(logsMap) } // retrieve all statefulsets @@ -39,26 +40,28 @@ func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigConte api.LogDebugMessage("Number of statefulsets found: %d\n", statefulsets.Size()) for _, statefulset := range statefulsets.Items { api.LogDebugMessage("Statefulset name: %s\n", statefulset.GetName()) - if logs, err = p.CG.GetPodsAndPodLogsFromFailedInitContainer(clientset, statefulset.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { + if logsMap, err = p.CG.GetPodsAndPodLogsFromFailedInitContainer(clientset, statefulset.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { fmt.Printf("%s\n", err) return err } - p.filterLogsForErrors(logs) + p.filterLogsForErrors(logsMap) } return nil } -func (p *QliksensePostflight) filterLogsForErrors(logs string) { - containerLogs := strings.Split(logs, "\n") - if len(containerLogs) > 0 { - for _, logLine := range containerLogs { - api.LogDebugMessage("init container logs: \n") - if strings.Contains(strings.ToLower(logLine), "error") { - fmt.Printf("%s\n", logLine) +func (p *QliksensePostflight) filterLogsForErrors(logsMap map[string]string) { + for podName, podLog := range logsMap { + containerLogs := strings.Split(podLog, "\n") + if len(containerLogs) > 0 { + for _, logLine := range containerLogs { + api.LogDebugMessage("init container logs: \n") + if strings.Contains(strings.ToLower(logLine), "error") { + fmt.Printf("Logs from pod: %s\n%s\n", podName, logLine) + } } + } else { + p.CG.LogVerboseMessage("no logs obtained\n") } - } else { - p.CG.LogVerboseMessage("no logs obtained\n") } } From 052dc71e7dcaf602d2780167387ba4b3cb729332 Mon Sep 17 00:00:00 2001 From: Ashwathi Shiva Date: Sun, 31 May 2020 21:47:02 -0400 Subject: [PATCH 6/9] updated doc --- docs/command_reference.md | 10 +++++++++- docs/postflight_checks.md | 33 +++++++++++++++++++++++++++++++++ docs/preflight_checks.md | 2 +- 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 docs/postflight_checks.md diff --git a/docs/command_reference.md b/docs/command_reference.md index 531b504f..4fce472f 100644 --- a/docs/command_reference.md +++ b/docs/command_reference.md @@ -4,7 +4,7 @@ Preflight checks provide pre-installation cluster conformance testing and validation before we install qliksense on the cluster. We gather a suite of conformance tests that can be easily written and run on the target cluster to verify that cluster-specific requirements are met. -We support the following tests at the moment as part of preflight checks, and the range of the suite will be expanded in future. +We support a couple of tests at the moment as part of preflight checks, and the range of the suite will be expanded in future. Run the following command to view help about the commands supported by preflight at any moment: ``` @@ -29,6 +29,14 @@ Run the following command to cleanup entities created for preflight checks that qliksense preflight clean ``` +### qliksense postflight +Postflight checks are performed after qliksense is installed on the cluster and during normal operating mode of the product. Such checks can range from validating certain conditions to checking the status of certain operations or entities. + +Run the following command to view help about the commands supported by postflight at any moment: +``` +qliksense postflight +``` + ### qliksense load `qliksense load` command takes input from a file or from pipe diff --git a/docs/postflight_checks.md b/docs/postflight_checks.md new file mode 100644 index 00000000..a872105f --- /dev/null +++ b/docs/postflight_checks.md @@ -0,0 +1,33 @@ +# Postflight checks +Postflight checks are performed after qliksense is installed on the cluster and during normal operating mode of the product. Such checks can range from validating certain conditions to checking the status of certain operations or entities on the kubernetes cluster. + +Run the following command to view help about the commands supported by postflight at any moment: +``` +$ qliksense postflight +perform postflight checks on the cluster + +Usage: + qliksense postflight [command] + +Examples: +qliksense postflight + +Available Commands: + db-migration-check check mongodb migration status on the cluster + +Flags: + -h, --help help for postflight + -v, --verbose verbose mode +``` + +### DB migration check +This command checks init containers for successful database migrarion completions, and reports failure, if any to the user. + +An example run of this check produces an output as shown below: + +```shell +$ qliksense postflight db-migration-check +Logs from pod: qliksense-users-6977cb7788-cxxwh +{"caller":"main.go:39","environment":"qseok","error":"error parsing uri: scheme must be \"mongodb\" or \"mongodb+srv\"","level":"error","message":"failed to connect to ","timestamp":"2020-06-01T01:07:18.4170507Z","version":""} +Postflight db_migration_check completed +``` \ No newline at end of file diff --git a/docs/preflight_checks.md b/docs/preflight_checks.md index 7970311b..4dfd8779 100644 --- a/docs/preflight_checks.md +++ b/docs/preflight_checks.md @@ -2,7 +2,7 @@ Preflight checks provide pre-installation cluster conformance testing and validation before we install qliksense on the cluster. We gather a suite of conformance tests that can be easily written and run on the target cluster to verify that cluster-specific requirements are met. -We support the following tests at the moment as part of preflight checks, and the range of the suite will be expanded in future. +We support a couple of tests at the moment as part of preflight checks, and the range of the suite will be expanded in future. Run the following command to view help about the commands supported by preflight at any moment: ```shell From 3f9868018793ad529e6278c39faff5dc7e7d3c8a Mon Sep 17 00:00:00 2001 From: Ashwathi Shiva Date: Mon, 1 Jun 2020 00:03:19 -0400 Subject: [PATCH 7/9] added some tests --- cmd/qliksense/postflight.go | 3 +- pkg/api/clientgo_utils_test.go | 139 +++++++++++++++++++++++++-- pkg/postflight/db_migration_check.go | 4 +- 3 files changed, 136 insertions(+), 10 deletions(-) diff --git a/cmd/qliksense/postflight.go b/cmd/qliksense/postflight.go index 060773c4..75c2a601 100644 --- a/cmd/qliksense/postflight.go +++ b/cmd/qliksense/postflight.go @@ -5,6 +5,7 @@ import ( . "github.com/logrusorgru/aurora" ansi "github.com/mattn/go-colorable" + "github.com/qlik-oss/sense-installer/pkg/api" postflight "github.com/qlik-oss/sense-installer/pkg/postflight" "github.com/qlik-oss/sense-installer/pkg/qliksense" "github.com/spf13/cobra" @@ -32,7 +33,7 @@ func pfMigrationCheck(q *qliksense.Qliksense) *cobra.Command { Long: `check mongodb migration status on the cluster`, Example: `qliksense postflight db-migration-check`, RunE: func(cmd *cobra.Command, args []string) error { - pf := &postflight.QliksensePostflight{Q: q, P: postflightOpts} + pf := &postflight.QliksensePostflight{Q: q, P: postflightOpts, CG: &api.ClientGoUtils{Verbose: postflightOpts.Verbose}} // Postflight db_migration_check namespace, kubeConfigContents, err := pf.CG.LoadKubeConfigAndNamespace() diff --git a/pkg/api/clientgo_utils_test.go b/pkg/api/clientgo_utils_test.go index 6d5e6ac6..1053bc8d 100644 --- a/pkg/api/clientgo_utils_test.go +++ b/pkg/api/clientgo_utils_test.go @@ -1,14 +1,18 @@ package api import ( + "errors" "reflect" "testing" appsv1 "k8s.io/api/apps/v1" apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" + k8stesting "k8s.io/client-go/testing" ) func TestClientGoUtils_getDeployment(t *testing.T) { @@ -28,7 +32,7 @@ func TestClientGoUtils_getDeployment(t *testing.T) { wantErr bool }{ { - name: "valid case", + name: "retrieve valid deployment", fields: fields{Verbose: true}, args: args{ clientset: fake.NewSimpleClientset(&appsv1.Deployment{ @@ -49,7 +53,7 @@ func TestClientGoUtils_getDeployment(t *testing.T) { wantErr: false, }, { - name: "error case", + name: "retrieve non-existent deployment", fields: fields{Verbose: true}, args: args{ clientset: fake.NewSimpleClientset(), @@ -93,7 +97,7 @@ func TestClientGoUtils_DeleteDeployment(t *testing.T) { wantErr bool }{ { - name: "valid case", + name: "delete valid deployment", fields: fields{Verbose: true}, args: args{ clientset: fake.NewSimpleClientset(&appsv1.Deployment{ @@ -108,7 +112,7 @@ func TestClientGoUtils_DeleteDeployment(t *testing.T) { wantErr: false, }, { - name: "valid case", + name: "delete non-existent deployment", fields: fields{Verbose: true}, args: args{ clientset: fake.NewSimpleClientset(), @@ -147,16 +151,36 @@ func TestClientGoUtils_GetService(t *testing.T) { wantErr bool }{ { - name: "valid case", + name: "retrieve valid service", fields: fields{Verbose: true}, - args: args{}, - want: &appsv1.Service{ + args: args{ + clientset: fake.NewSimpleClientset(&apiv1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-svc", + Namespace: "test-ns", + }, + }), + namespace: "test-ns", + svcName: "test-svc", + }, + want: &apiv1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test-svc", Namespace: "test-ns", }, - wantErr: false, }, + wantErr: false, + }, + { + name: "retrieve non-existent service", + fields: fields{Verbose: true}, + args: args{ + clientset: fake.NewSimpleClientset(), + namespace: "test-ns", + svcName: "test-svc", + }, + want: nil, + wantErr: true, }, } for _, tt := range tests { @@ -175,3 +199,102 @@ func TestClientGoUtils_GetService(t *testing.T) { }) } } + +func TestClientGoUtils_CreatePreflightTestDeployment(t *testing.T) { + fk := fake.NewSimpleClientset() + fk.Fake.PrependReactor("create", "deployments", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &appsv1.Deployment{}, errors.New("Error creating deployment") + }) + + type fields struct { + Verbose bool + } + type args struct { + clientset kubernetes.Interface + namespace string + depName string + imageName string + } + tests := []struct { + name string + fields fields + args args + want *appsv1.Deployment + wantErr bool + }{ + { + name: "create valid deployment", + fields: fields{Verbose: true}, + args: args{ + clientset: fake.NewSimpleClientset(), + namespace: "test-ns", + depName: "test-dep", + imageName: "nginx", + }, + want: &appsv1.Deployment{ + ObjectMeta: v1.ObjectMeta{ + Name: "test-dep", + }, + Spec: appsv1.DeploymentSpec{ + Replicas: int32Ptr(1), + Selector: &v1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "preflight-check", + }, + }, + Template: apiv1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: map[string]string{ + "app": "preflight-check", + "label": "preflight-check-label", + }, + }, + Spec: apiv1.PodSpec{ + Containers: []apiv1.Container{ + { + Name: "dep", + Image: "nginx", + Ports: []apiv1.ContainerPort{ + { + Name: "http", + Protocol: apiv1.ProtocolTCP, + ContainerPort: 80, + }, + }, + }, + }, + }, + }, + }, + }, + wantErr: false, + }, + { + name: "invalid case - create deployment", + fields: fields{Verbose: true}, + args: args{ + clientset: fk, + namespace: "test-ns", + depName: "test-dep", + imageName: "", + }, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &ClientGoUtils{ + Verbose: tt.fields.Verbose, + } + got, err := p.CreatePreflightTestDeployment(tt.args.clientset, tt.args.namespace, tt.args.depName, tt.args.imageName) + if (err != nil) != tt.wantErr { + t.Errorf("ClientGoUtils.CreatePreflightTestDeployment() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ClientGoUtils.CreatePreflightTestDeployment() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/postflight/db_migration_check.go b/pkg/postflight/db_migration_check.go index 5ae53685..f368f9c2 100644 --- a/pkg/postflight/db_migration_check.go +++ b/pkg/postflight/db_migration_check.go @@ -22,6 +22,7 @@ func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigConte var logsMap map[string]string // Retrieve all deployments + p.CG.LogVerboseMessage("Retrieving logs from deployments\n") deploymentsClient := clientset.AppsV1().Deployments(namespace) deployments, err := deploymentsClient.List(v1.ListOptions{}) api.LogDebugMessage("Number of deployments found: %d\n", deployments.Size()) @@ -35,6 +36,7 @@ func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigConte } // retrieve all statefulsets + p.CG.LogVerboseMessage("Retrieving logs from statefulsets\n") statefulsetsClient := clientset.AppsV1().StatefulSets(namespace) statefulsets, err := statefulsetsClient.List(v1.ListOptions{}) api.LogDebugMessage("Number of statefulsets found: %d\n", statefulsets.Size()) @@ -54,8 +56,8 @@ func (p *QliksensePostflight) filterLogsForErrors(logsMap map[string]string) { for podName, podLog := range logsMap { containerLogs := strings.Split(podLog, "\n") if len(containerLogs) > 0 { + p.CG.LogVerboseMessage("checking init container logs... \n") for _, logLine := range containerLogs { - api.LogDebugMessage("init container logs: \n") if strings.Contains(strings.ToLower(logLine), "error") { fmt.Printf("Logs from pod: %s\n%s\n", podName, logLine) } From d87a9e48bf17433d1d6b42cf4aaa12dfc3d4060d Mon Sep 17 00:00:00 2001 From: Ashwathi Shiva Date: Tue, 2 Jun 2020 00:12:28 -0400 Subject: [PATCH 8/9] added kubectl logs command after displaying error logs --- go.mod | 2 +- go.sum | 2 ++ pkg/postflight/db_migration_check.go | 14 +++++++++----- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 368b5bdd..1a5b8408 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( github.com/spf13/viper v1.6.1 golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 // indirect golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a // indirect - golang.org/x/net v0.0.0-20200301022130-244492dfa37a + golang.org/x/net v0.0.0-20200528225125-3c3fba18258b golang.org/x/tools v0.0.0-20200312194400-c312e98713c2 // indirect google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b // indirect gopkg.in/yaml.v2 v2.2.8 diff --git a/go.sum b/go.sum index 6621c194..351a23ba 100644 --- a/go.sum +++ b/go.sum @@ -1172,6 +1172,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200528225125-3c3fba18258b h1:IYiJPiJfzktmDAO1HQiwjMjwjlYKHAL7KzeD544RJPs= +golang.org/x/net v0.0.0-20200528225125-3c3fba18258b/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/pkg/postflight/db_migration_check.go b/pkg/postflight/db_migration_check.go index f368f9c2..98e58dcd 100644 --- a/pkg/postflight/db_migration_check.go +++ b/pkg/postflight/db_migration_check.go @@ -32,7 +32,7 @@ func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigConte fmt.Printf("%s\n", err) return err } - p.filterLogsForErrors(logsMap) + p.filterLogsForErrors(logsMap, namespace) } // retrieve all statefulsets @@ -46,24 +46,28 @@ func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigConte fmt.Printf("%s\n", err) return err } - p.filterLogsForErrors(logsMap) + p.filterLogsForErrors(logsMap, namespace) } return nil } -func (p *QliksensePostflight) filterLogsForErrors(logsMap map[string]string) { +func (p *QliksensePostflight) filterLogsForErrors(logsMap map[string]string, namespace string) { + errorLogsPresent := false for podName, podLog := range logsMap { containerLogs := strings.Split(podLog, "\n") if len(containerLogs) > 0 { - p.CG.LogVerboseMessage("checking init container logs... \n") for _, logLine := range containerLogs { if strings.Contains(strings.ToLower(logLine), "error") { + errorLogsPresent = true fmt.Printf("Logs from pod: %s\n%s\n", podName, logLine) } } + if errorLogsPresent { + fmt.Printf("To view more logs in this context, please run the command: kubectl logs -n %s %s %s\n", namespace, podName, initContainerNameToCheck) + } } else { - p.CG.LogVerboseMessage("no logs obtained\n") + fmt.Printf("no logs obtained\n\n") } } } From cb7669462386cdf37625672ce2cb4178747af434 Mon Sep 17 00:00:00 2001 From: Ashwathi Shiva Date: Wed, 3 Jun 2020 11:49:36 -0400 Subject: [PATCH 9/9] fixed typo: changed kube-version to k8s-version --- cmd/qliksense/preflight.go | 4 ++-- docs/preflight_checks.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/qliksense/preflight.go b/cmd/qliksense/preflight.go index 4d78fc34..048d4b2a 100644 --- a/cmd/qliksense/preflight.go +++ b/cmd/qliksense/preflight.go @@ -71,10 +71,10 @@ func pfK8sVersionCheckCmd(q *qliksense.Qliksense) *cobra.Command { } var preflightCheckK8sVersionCmd = &cobra.Command{ - Use: "kube-version", + Use: "k8s-version", Short: "check kubernetes version", Long: `check minimum valid kubernetes version on the cluster`, - Example: `qliksense preflight kube-version`, + Example: `qliksense preflight k8s-version`, RunE: func(cmd *cobra.Command, args []string) error { qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}} diff --git a/docs/preflight_checks.md b/docs/preflight_checks.md index 4dfd8779..8e572df7 100644 --- a/docs/preflight_checks.md +++ b/docs/preflight_checks.md @@ -21,7 +21,7 @@ Available Commands: clean perform preflight clean deployment perform preflight deployment check dns perform preflight dns check - kube-version check kubernetes version + k8s-version check kubernetes version mongo preflight mongo OR preflight mongo --url= pod perform preflight pod check role preflight create role check