diff --git a/cmd/qliksense/postflight.go b/cmd/qliksense/postflight.go new file mode 100644 index 00000000..75c2a601 --- /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, CG: &api.ClientGoUtils{Verbose: postflightOpts.Verbose}} + + // Postflight db_migration_check + 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) + 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 completed")) + 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..048d4b2a 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 := qp.CG.LoadKubeConfigAndNamespace() if err != nil { fmt.Fprintf(out, "%s\n", Red("Preflight DNS check FAILED")) fmt.Printf("Error: %v\n", err) @@ -70,15 +71,15 @@ 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} + 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 := qp.CG.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 := 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) @@ -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 := qp.CG.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 := qp.CG.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 := qp.CG.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 := qp.CG.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 := qp.CG.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 := qp.CG.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 := qp.CG.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 := qp.CG.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 := qp.CG.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 f29b936b..b72e0551 100644 --- a/cmd/qliksense/root.go +++ b/cmd/qliksense/root.go @@ -211,7 +211,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)) @@ -229,6 +229,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/docs/command_reference.md b/docs/command_reference.md index 81763ff8..b04f96af 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..8e572df7 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 @@ -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 diff --git a/go.mod b/go.mod index 2ad328b4..4e26f69d 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 bca18b50..151b053f 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= @@ -1051,6 +1053,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= @@ -1171,6 +1174,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/api/clientgo_utils.go b/pkg/api/clientgo_utils.go new file mode 100644 index 00000000..7452db93 --- /dev/null +++ b/pkg/api/clientgo_utils.go @@ -0,0 +1,874 @@ +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 (p *ClientGoUtils) LoadKubeConfigAndNamespace() (string, []byte, error) { + LogDebugMessage("Reading .kube/config file...") + + homeDir, err := homedir.Dir() + if err != nil { + err = fmt.Errorf("Unable to deduce home dir") + return "", nil, err + } + 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") + 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 (p *ClientGoUtils) 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 (p *ClientGoUtils) 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 (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{ + 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 := 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 + } + p.LogVerboseMessage("Created deployment %q\n", result.GetObjectMeta().GetName()) + + return deployment, nil +} + +func (p *ClientGoUtils) getDeployment(clientset kubernetes.Interface, namespace, depName string) (*appsv1.Deployment, error) { + deploymentsClient := clientset.AppsV1().Deployments(namespace) + var deployment *appsv1.Deployment + if err := p.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 (p *ClientGoUtils) DeleteDeployment(clientset kubernetes.Interface, namespace, name string) error { + deploymentsClient := clientset.AppsV1().Deployments(namespace) + // Create Deployment + deletePolicy := v1.DeletePropagationForeground + deleteOptions := v1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + GracePeriodSeconds: &gracePeriod, + } + + if err := p.RetryOnError(func() (err error) { + return deploymentsClient.Delete(name, &deleteOptions) + }); err != nil { + return err + } + if err := p.WaitForDeploymentToDelete(clientset, namespace, name); err != nil { + return err + } + p.LogVerboseMessage("Deleted deployment: %s\n", name) + return nil +} + +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{ + 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 := p.RetryOnError(func() (err error) { + result, err = servicesClient.Create(service) + return err + }); err != nil { + return nil, err + } + p.LogVerboseMessage("Created service %q\n", result.GetObjectMeta().GetName()) + + return service, nil +} + +func (p *ClientGoUtils) GetService(clientset kubernetes.Interface, namespace, svcName string) (*apiv1.Service, error) { + servicesClient := clientset.CoreV1().Services(namespace) + var svc *apiv1.Service + if err := p.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 (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 := p.RetryOnError(func() (err error) { + return servicesClient.Delete(name, &deleteOptions) + }); err != nil { + return err + } + p.LogVerboseMessage("Deleted service: %s\n", name) + return nil +} + +func (p *ClientGoUtils) DeletePod(clientset kubernetes.Interface, namespace, name string) error { + + podsClient := clientset.CoreV1().Pods(namespace) + deletePolicy := v1.DeletePropagationForeground + deleteOptions := v1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + GracePeriodSeconds: &gracePeriod, + } + if err := p.RetryOnError(func() (err error) { + return podsClient.Delete(name, &deleteOptions) + }); err != nil { + return err + } + if err := p.waitForPodToDelete(clientset, namespace, name); err != nil { + return err + } + p.LogVerboseMessage("Deleted pod: %s\n", name) + return nil +} + +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{ + 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 := p.RetryOnError(func() (err error) { + pod, err = clientset.CoreV1().Pods(namespace).Create(pod) + return err + }); err != nil { + return nil, err + } + p.LogVerboseMessage("Created pod: %s\n", pod.Name) + return pod, nil +} + +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 := p.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 (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) + 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 (p *ClientGoUtils) 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 (p *ClientGoUtils) WaitForDeployment(clientset kubernetes.Interface, namespace string, pfDeployment *appsv1.Deployment) error { + var err error + depName := pfDeployment.GetName() + checkFunc := func() (interface{}, error) { + pfDeployment, err = p.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 := p.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 (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") + return err + } + podName := pod.Name + checkFunc := func() (interface{}, error) { + pod, err = p.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 := p.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 (p *ClientGoUtils) WaitForPodToDie(clientset kubernetes.Interface, namespace string, pod *apiv1.Pod) error { + podName := pod.Name + checkFunc := func() (interface{}, error) { + po, err := p.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 := p.waitForResource(checkFunc, validateFunc); err != nil { + return err + } + return nil +} + +func (p *ClientGoUtils) waitForPodToDelete(clientset kubernetes.Interface, namespace, podName string) error { + checkFunc := func() (interface{}, error) { + po, err := p.getPod(clientset, namespace, podName) + if err != nil { + return nil, err + } + return po, nil + } + validateFunc := func(po interface{}) bool { + return false + } + if err := p.waitForResource(checkFunc, validateFunc); err != nil { + return nil + } + err := fmt.Errorf("delete pod is taking unusually long") + return err +} + +func (p *ClientGoUtils) WaitForDeploymentToDelete(clientset kubernetes.Interface, namespace, deploymentName string) error { + checkFunc := func() (interface{}, error) { + dep, err := p.getDeployment(clientset, namespace, deploymentName) + if err != nil { + return nil, err + } + return dep, nil + } + validateFunc := func(po interface{}) bool { + return false + } + if err := p.waitForResource(checkFunc, validateFunc); err != nil { + return nil + } + err := fmt.Errorf("delete deployment is taking unusually long") + return err +} + +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{ + 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 := p.RetryOnError(func() (err error) { + role, err = clientset.RbacV1beta1().Roles(namespace).Create(roleSpec) + return err + }); err != nil { + return nil, err + } + + p.LogVerboseMessage("Created role: %s\n", role.Name) + + return role, nil +} + +func (p *ClientGoUtils) DeleteRole(clientset kubernetes.Interface, 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 + } + p.LogVerboseMessage("Deleted role: %s\n\n", roleName) + return nil +} + +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{ + 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 := p.RetryOnError(func() (err error) { + roleBinding, err = clientset.RbacV1beta1().RoleBindings(namespace).Create(roleBindingSpec) + return err + }); err != nil { + return nil, err + } + p.LogVerboseMessage("Created RoleBinding: %s\n", roleBindingSpec.Name) + return roleBinding, nil +} + +func (p *ClientGoUtils) DeleteRoleBinding(clientset kubernetes.Interface, 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 + } + p.LogVerboseMessage("Deleted RoleBinding: %s\n\n", roleBindingName) + return nil +} + +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{ + 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 := p.RetryOnError(func() (err error) { + serviceAccount, err = clientset.CoreV1().ServiceAccounts(namespace).Create(serviceAccountSpec) + return err + }); err != nil { + return nil, err + } + p.LogVerboseMessage("Created Service Account: %s\n", serviceAccountSpec.Name) + return serviceAccount, nil +} + +func (p *ClientGoUtils) DeleteServiceAccount(clientset kubernetes.Interface, 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 + } + p.LogVerboseMessage("Deleted ServiceAccount: %s\n\n", serviceAccountName) + return nil +} + +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 + 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 = p.RetryOnError(func() (err error) { + secret, err = clientset.CoreV1().Secrets(namespace).Create(secretSpec) + return err + }); err != nil { + return nil, err + } + p.LogVerboseMessage("Created Secret: %s\n", secret.Name) + return secret, nil +} + +func (p *ClientGoUtils) DeleteK8sSecret(clientset kubernetes.Interface, 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 + } + p.LogVerboseMessage("Deleted Secret: %s\n", secretName) + return nil +} + +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{ + 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 Statefulset + var result *appsv1.StatefulSet + if err := p.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 + } + LogDebugMessage("Created statefulset %q\n", result.GetObjectMeta().GetName()) + + return statefulset, nil +} + +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) + for _, pod := range pods.Items { + LogDebugMessage("pod: %v\n", pod.Name) + } + return pods, err +} + +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 = p.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 := p.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 (p *ClientGoUtils) getStatefulset(clientset kubernetes.Interface, namespace, statefulsetName string) (*appsv1.StatefulSet, error) { + statefulsetsClient := clientset.AppsV1().StatefulSets(namespace) + var statefulset *appsv1.StatefulSet + if err := p.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 (p *ClientGoUtils) deleteStatefulSet(clientset kubernetes.Interface, namespace, name string) error { + statefulsetClient := clientset.AppsV1().StatefulSets(namespace) + + deletePolicy := v1.DeletePropagationForeground + deleteOptions := v1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + GracePeriodSeconds: &gracePeriod, + } + + if err := p.RetryOnError(func() (err error) { + return statefulsetClient.Delete(name, &deleteOptions) + }); err != nil { + return err + } + if err := p.waitForStatefulsetToDelete(clientset, namespace, name); err != nil { + return err + } + LogDebugMessage("Deleted statefulset: %s\n", name) + return nil +} + +func (p *ClientGoUtils) waitForStatefulsetToDelete(clientset kubernetes.Interface, namespace, statefulsetName string) error { + checkFunc := func() (interface{}, error) { + statefulset, err := p.getStatefulset(clientset, namespace, statefulsetName) + if err != nil { + return nil, err + } + return statefulset, nil + } + validateFunc := func(po interface{}) bool { + return false + } + if err := p.waitForResource(checkFunc, validateFunc); err != nil { + return nil + } + err := fmt.Errorf("delete statefulset is taking unusually long") + return err +} + +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) + if err != nil { + err = fmt.Errorf("unable to get podlist: %v", err) + fmt.Printf("%s\n", err) + } + LogDebugMessage("%d Pods retrieved\n ", len(podList.Items)) + + // var logs map[string]string + logs := map[string]string{} + for _, pod := range podList.Items { + 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[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 nil, err + } + } + } + } + return logs, nil +} diff --git a/pkg/api/clientgo_utils_test.go b/pkg/api/clientgo_utils_test.go new file mode 100644 index 00000000..1053bc8d --- /dev/null +++ b/pkg/api/clientgo_utils_test.go @@ -0,0 +1,300 @@ +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) { + 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: "retrieve valid deployment", + 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: "retrieve non-existent deployment", + 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: "delete valid deployment", + 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: "delete non-existent deployment", + 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) + } + }) + } +} + +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: "retrieve valid service", + fields: fields{Verbose: true}, + 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, + }, + { + 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 { + 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) + } + }) + } +} + +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 new file mode 100644 index 00000000..98e58dcd --- /dev/null +++ b/pkg/postflight/db_migration_check.go @@ -0,0 +1,73 @@ +package postflight + +import ( + "fmt" + "strings" + + "github.com/qlik-oss/sense-installer/pkg/api" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const initContainerNameToCheck = "migration" + +func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigContents []byte) error { + + 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) + return err + } + + 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()) + for _, deployment := range deployments.Items { + api.LogDebugMessage("Deployment name: %s\n", deployment.GetName()) + if logsMap, err = p.CG.GetPodsAndPodLogsFromFailedInitContainer(clientset, deployment.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { + fmt.Printf("%s\n", err) + return err + } + p.filterLogsForErrors(logsMap, namespace) + } + + // 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()) + for _, statefulset := range statefulsets.Items { + api.LogDebugMessage("Statefulset name: %s\n", statefulset.GetName()) + if logsMap, err = p.CG.GetPodsAndPodLogsFromFailedInitContainer(clientset, statefulset.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil { + fmt.Printf("%s\n", err) + return err + } + p.filterLogsForErrors(logsMap, namespace) + } + + return nil +} + +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 { + 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 { + fmt.Printf("no logs obtained\n\n") + } + } +} 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..c35e5807 100644 --- a/pkg/preflight/deployability.go +++ b/pkg/preflight/deployability.go @@ -6,8 +6,8 @@ import ( "k8s.io/client-go/kubernetes" ) -func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigContents []byte, cleanup bool) error { - clientset, _, err := 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 @@ -15,142 +15,142 @@ func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigConten // Deployment check if !cleanup { - qp.P.LogVerboseMessage("Preflight deployment check: \n") - qp.P.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.P.LogVerboseMessage("Preflight Deployment check: FAILED\n") + p.CG.LogVerboseMessage("Preflight Deployment check: FAILED\n") return err } if !cleanup { - qp.P.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 := 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.P.LogVerboseMessage("Preflight service check: \n") - qp.P.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.P.LogVerboseMessage("Preflight Service check: FAILED\n") + p.CG.LogVerboseMessage("Preflight Service check: FAILED\n") return err } if !cleanup { - qp.P.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 := 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.P.LogVerboseMessage("Preflight pod check: \n") - qp.P.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.P.LogVerboseMessage("Preflight Pod check: FAILED\n") + p.CG.LogVerboseMessage("Preflight Pod check: FAILED\n") return err } if !cleanup { - qp.P.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.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" - qp.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.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.deletePod(clientset, namespace, podName) + defer p.CG.DeletePod(clientset, namespace, podName) - if err := waitForPod(clientset, namespace, pod); err != nil { + if err := p.CG.WaitForPod(clientset, namespace, pod); err != nil { return err } - qp.P.LogVerboseMessage("Preflight pod creation check: PASSED\n") - qp.P.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.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" - qp.deleteService(clientset, namespace, serviceName) + p.CG.DeleteService(clientset, namespace, serviceName) if cleanup { return nil } // creating service - pfService, err := qp.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.deleteService(clientset, namespace, serviceName) - _, err = 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.P.LogVerboseMessage("Preflight service creation check: PASSED\n") - qp.P.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.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" - qp.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.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.deleteDeployment(clientset, namespace, depName) - if err := 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.P.LogVerboseMessage("Preflight Deployment check: PASSED\n") - qp.P.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 321ea246..e7ee14bf 100644 --- a/pkg/preflight/dns_check.go +++ b/pkg/preflight/dns_check.go @@ -12,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.P.LogVerboseMessage("Preflight DNS check: \n") - qp.P.LogVerboseMessage("------------------- \n") + p.CG.LogVerboseMessage("Preflight DNS check: \n") + p.CG.LogVerboseMessage("------------------- \n") } - clientset, _, err := 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.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.deleteDeployment(clientset, namespace, depName) + defer p.CG.DeleteDeployment(clientset, namespace, depName) - if err := waitForDeployment(clientset, namespace, dnsDeployment); err != nil { + if err := p.CG.WaitForDeployment(clientset, namespace, dnsDeployment); err != nil { return err } // creating service - dnsService, err := qp.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.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.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.deletePod(clientset, namespace, podName) + defer p.CG.DeletePod(clientset, namespace, podName) - if err := waitForPod(clientset, namespace, dnsPod); err != nil { + if err := p.CG.WaitForPod(clientset, namespace, dnsPod); err != nil { return err } if len(dnsPod.Spec.Containers) == 0 { @@ -81,30 +81,30 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by return err } - waitForPodToDie(clientset, namespace, dnsPod) + p.CG.WaitForPodToDie(clientset, namespace, dnsPod) - logStr, err := 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.P.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.P.LogVerboseMessage("Completed preflight DNS check\n") - qp.P.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.deleteDeployment(clientset, namespace, depName) - qp.deletePod(clientset, namespace, podName) - qp.deleteService(clientset, namespace, serviceName) +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 30476080..5cf577d1 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,34 +71,34 @@ 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 } -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 := 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.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.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.deletePod(clientset, namespace, mongoPodName) + defer p.CG.DeletePod(clientset, namespace, mongoPodName) - if err := 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 } - waitForPodToDie(clientset, namespace, mongoPod) - logStr, err := 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.P.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.P.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.Interface, namespace, certFile, certSecretName string) (*apiv1.Secret, error) { certBytes, err := ioutil.ReadFile(certFile) if err != nil { return nil, err } - certSecret, err := qp.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.deletePod(clientset, namespace, mongoPodName) - qp.deleteK8sSecret(clientset, namespace, caCertSecretName) +func (p *QliksensePreflight) runMongoCleanup(clientset kubernetes.Interface, namespace, mongoPodName, caCertSecretName string) { + p.CG.DeletePod(clientset, namespace, mongoPodName) + p.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..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.P.LogVerboseMessage("Preflight kubernetes version check: \n") - qp.P.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 := 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 := 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.P.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.P.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 diff --git a/pkg/qliksense/docker_test.go b/pkg/qliksense/docker_test.go index 0af29ab0..4c4be58c 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") } }