Compare commits

...

17 Commits

Author SHA1 Message Date
Foysal Iqbal
99b442020f add dry-run for debugging purpose
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-02 11:05:41 -04:00
Foysal Iqbal
9c6e83152d add dry-run for debugging purpose
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-02 10:27:44 -04:00
Foysal Iqbal
a9a9d464c0 Merge branch 'master' into dry-run 2020-06-02 09:42:33 -04:00
Foysal Iqbal
9d0ac0290f add unset command (#381) 2020-06-01 00:08:08 -04:00
Foysal Iqbal
ef3224fb2c add dry-run for debugging purpose
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-05-28 22:41:57 -04:00
Andriy Bulynko
dd8a48b2b8 Fixing a test expecting a "v" at the start of the operator docker image version (#379) 2020-05-27 16:26:36 -04:00
Andriy Bulynko
9fb6800993 Upgrading kustomize to qlik/v0.0.25 (#378) 2020-05-27 15:19:20 -04:00
Ashwathi Shiva
bbb811a879 Preflight mongo mutual tls (#365)
* mongo check working when ca cert and client cert put in same file
* updated code to use image from bintray
2020-05-13 20:36:07 -04:00
Foysal Iqbal
8156b884ce Fix cipher (#363) 2020-05-12 09:54:04 -04:00
Ashwathi Shiva
7525c2e698 Preflight mongo mutual tls (#357)
* preflight mongo mutual tls working when cert is specified in CR
2020-05-04 09:42:43 -04:00
Andriy Bulynko
60763e034a Not deleting docker pull secret (#356) 2020-04-29 09:33:41 -04:00
Ashwathi Shiva
ce4081a422 Preflight mongo version check (#353)
* mongo minimum version check working
2020-04-28 16:51:02 -04:00
Sanat Nayar
dd503a40c1 Merge pull request #347 from qlik-oss/upgrade_libraries
upgrade/standardize yaml and semver
2020-04-28 09:02:10 -04:00
Sanat Nayar
b790419fc2 Merge branch 'master' into upgrade_libraries 2020-04-27 16:03:25 -04:00
Ashwathi Shiva
55f9c07c21 Preflight clean cmd (#345)
* Preflight clean as a command and cleanup before and after individual preflight checks
2020-04-27 10:26:50 -04:00
Sanat Nayar
ef77ea3a5f init 2020-04-27 08:49:42 -04:00
Sanat Nayar
7f70bfc7de init 2020-04-27 08:04:13 -04:00
33 changed files with 814 additions and 325 deletions

View File

@@ -37,7 +37,7 @@ func applyCmd(q *qliksense.Qliksense) *cobra.Command {
f.StringVarP(&filePath, "file", "f", "", "Install from a CR file") f.StringVarP(&filePath, "file", "f", "", "Install from a CR file")
c.MarkFlagRequired("file") c.MarkFlagRequired("file")
f.StringVarP(&opts.StorageClass, "storageClass", "s", "", "Storage class for qliksense") f.StringVarP(&opts.StorageClass, "storageClass", "s", "", "Storage class for qliksense")
f.StringVarP(&opts.MongoDbUri, "mongoDbUri", "m", "", "mongoDbUri for qliksense (i.e. mongodb://qlik-default-mongodb:27017/qliksense?ssl=false)") f.StringVarP(&opts.MongodbUri, "mongodbUri", "m", "", "mongodbUri for qliksense (i.e. mongodb://qlik-default-mongodb:27017/qliksense?ssl=false)")
f.StringVarP(&opts.RotateKeys, "rotateKeys", "r", "", "Rotate JWT keys for qliksense (yes:rotate keys/ no:use exising keys from cluster/ None: use default EJSON_KEY from env") f.StringVarP(&opts.RotateKeys, "rotateKeys", "r", "", "Rotate JWT keys for qliksense (yes:rotate keys/ no:use exising keys from cluster/ None: use default EJSON_KEY from env")
f.BoolVar(&keepPatchFiles, keepPatchFilesFlagName, keepPatchFiles, keepPatchFilesFlagUsage) f.BoolVar(&keepPatchFiles, keepPatchFilesFlagName, keepPatchFiles, keepPatchFilesFlagUsage)
f.BoolVarP(&pull, pullFlagName, pullFlagShorthand, pull, pullFlagUsage) f.BoolVarP(&pull, pullFlagName, pullFlagShorthand, pull, pullFlagUsage)

View File

@@ -220,3 +220,27 @@ func cleanConfigRepoPatchesCmd(q *qliksense.Qliksense) *cobra.Command {
}, },
} }
} }
func unsetCmd(q *qliksense.Qliksense) *cobra.Command {
cmd := &cobra.Command{
Use: "unset",
Short: "remove a key from a context or a secrets or a configs from the context",
Example: `
# remove the key from CR
qliksense config unset <key>
# remove the key from service inside configs/secrets of CR
qliksense config unset <service>.<key>
# remove the service from inside configs/secrets of CR
qliksense config usnet <servcie>
all of the above supports space separated multiple arguments
`,
RunE: func(cmd *cobra.Command, args []string) error {
return q.UnsetCmd(args)
},
Args: cobra.MinimumNArgs(1),
}
return cmd
}

View File

@@ -71,11 +71,13 @@ func installCmd(q *qliksense.Qliksense) *cobra.Command {
f := c.Flags() f := c.Flags()
f.StringVarP(&opts.StorageClass, "storageClass", "s", "", "Storage class for qliksense") f.StringVarP(&opts.StorageClass, "storageClass", "s", "", "Storage class for qliksense")
f.StringVarP(&opts.MongoDbUri, "mongoDbUri", "m", "", "mongoDbUri for qliksense (i.e. mongodb://qlik-default-mongodb:27017/qliksense?ssl=false)") f.StringVarP(&opts.MongodbUri, "mongodbUri", "m", "", "mongodbUri for qliksense (i.e. mongodb://qlik-default-mongodb:27017/qliksense?ssl=false)")
f.StringVarP(&opts.RotateKeys, "rotateKeys", "r", "", "Rotate JWT keys for qliksense (yes:rotate keys/ no:use exising keys from cluster/ None: use default EJSON_KEY from env") f.StringVarP(&opts.RotateKeys, "rotateKeys", "r", "", "Rotate JWT keys for qliksense (yes:rotate keys/ no:use exising keys from cluster/ None: use default EJSON_KEY from env")
f.BoolVar(&keepPatchFiles, keepPatchFilesFlagName, keepPatchFiles, keepPatchFilesFlagUsage) f.BoolVar(&keepPatchFiles, keepPatchFilesFlagName, keepPatchFiles, keepPatchFilesFlagUsage)
f.StringVarP(&filePath, "file", "f", "", "Install from a CR file") f.StringVarP(&filePath, "file", "f", "", "Install from a CR file")
f.BoolVarP(&opts.DryRun, "dry-run", "", false, "Dry run will generate the patches without rotating keys")
f.BoolVarP(&pull, pullFlagName, pullFlagShorthand, pull, pullFlagUsage) f.BoolVarP(&pull, pullFlagName, pullFlagShorthand, pull, pullFlagUsage)
f.BoolVarP(&push, pushFlagName, pushFlagShorthand, push, pushFlagUsage) f.BoolVarP(&push, pushFlagName, pushFlagShorthand, push, pushFlagUsage)

View File

@@ -3,21 +3,26 @@ package main
import ( import (
"fmt" "fmt"
. "github.com/logrusorgru/aurora"
ansi "github.com/mattn/go-colorable" ansi "github.com/mattn/go-colorable"
"github.com/qlik-oss/sense-installer/pkg/preflight" "github.com/qlik-oss/sense-installer/pkg/preflight"
. "github.com/logrusorgru/aurora"
"github.com/qlik-oss/sense-installer/pkg/qliksense" "github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func preflightCmd(q *qliksense.Qliksense) *cobra.Command { func preflightCmd(q *qliksense.Qliksense) *cobra.Command {
preflightOpts := &preflight.PreflightOptions{
MongoOptions: &preflight.MongoOptions{},
}
var preflightCmd = &cobra.Command{ var preflightCmd = &cobra.Command{
Use: "preflight", Use: "preflight",
Short: "perform preflight checks on the cluster", Short: "perform preflight checks on the cluster",
Long: `perform preflight checks on the cluster`, Long: `perform preflight checks on the cluster`,
Example: `qliksense preflight <preflight_check_to_run>`, Example: `qliksense preflight <preflight_check_to_run>`,
} }
f := preflightCmd.Flags()
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
return preflightCmd return preflightCmd
} }
@@ -44,7 +49,7 @@ func pfDnsCheckCmd(q *qliksense.Qliksense) *cobra.Command {
if namespace == "" { if namespace == "" {
namespace = "default" namespace = "default"
} }
if err = qp.CheckDns(namespace, kubeConfigContents); err != nil { if err = qp.CheckDns(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight DNS check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight DNS check FAILED"))
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
return nil return nil
@@ -131,12 +136,7 @@ func pfAllChecksCmd(q *qliksense.Qliksense) *cobra.Command {
f := preflightAllChecksCmd.Flags() f := preflightAllChecksCmd.Flags()
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode") f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
f.StringVarP(&preflightOpts.MongoOptions.MongodbUrl, "mongodb-url", "", "", "mongodbUrl to try connecting to") f.StringVarP(&preflightOpts.MongoOptions.MongodbUrl, "mongodb-url", "", "", "mongodbUrl to try connecting to")
f.StringVarP(&preflightOpts.MongoOptions.Username, "mongodb-username", "", "", "username to connect to mongodb")
f.StringVarP(&preflightOpts.MongoOptions.Password, "mongodb-password", "", "", "password to connect to mongodb")
f.StringVarP(&preflightOpts.MongoOptions.CaCertFile, "mongodb-ca-cert", "", "", "certificate to use for mongodb check") f.StringVarP(&preflightOpts.MongoOptions.CaCertFile, "mongodb-ca-cert", "", "", "certificate to use for mongodb check")
f.StringVarP(&preflightOpts.MongoOptions.ClientCertFile, "mongodb-client-cert", "", "", "client-certificate to use for mongodb check")
f.BoolVar(&preflightOpts.MongoOptions.Tls, "mongodb-tls", false, "enable tls?")
return preflightAllChecksCmd return preflightAllChecksCmd
} }
@@ -147,7 +147,7 @@ func pfDeploymentCheckCmd(q *qliksense.Qliksense) *cobra.Command {
} }
var pfDeploymentCheckCmd = &cobra.Command{ var pfDeploymentCheckCmd = &cobra.Command{
Use: "deployment", Use: "deployment",
Short: "perform preflight deploymwnt check", Short: "perform preflight deployment check",
Long: `perform preflight deployment check to ensure that we can create deployments in the cluster`, Long: `perform preflight deployment check to ensure that we can create deployments in the cluster`,
Example: `qliksense preflight deployment`, Example: `qliksense preflight deployment`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
@@ -163,7 +163,7 @@ func pfDeploymentCheckCmd(q *qliksense.Qliksense) *cobra.Command {
if namespace == "" { if namespace == "" {
namespace = "default" namespace = "default"
} }
if err = qp.CheckDeployment(namespace, kubeConfigContents); err != nil { if err = qp.CheckDeployment(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED"))
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
return nil return nil
@@ -202,7 +202,7 @@ func pfServiceCheckCmd(q *qliksense.Qliksense) *cobra.Command {
if namespace == "" { if namespace == "" {
namespace = "default" namespace = "default"
} }
if err = qp.CheckService(namespace, kubeConfigContents); err != nil { if err = qp.CheckService(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED"))
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
return nil return nil
@@ -240,7 +240,7 @@ func pfPodCheckCmd(q *qliksense.Qliksense) *cobra.Command {
if namespace == "" { if namespace == "" {
namespace = "default" namespace = "default"
} }
if err = qp.CheckPod(namespace, kubeConfigContents); err != nil { if err = qp.CheckPod(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED"))
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
return nil return nil
@@ -275,7 +275,7 @@ func pfCreateRoleCheckCmd(q *qliksense.Qliksense) *cobra.Command {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
return nil return nil
} }
if err = qp.CheckCreateRole(namespace); err != nil { if err = qp.CheckCreateRole(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED"))
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
return nil return nil
@@ -310,7 +310,7 @@ func pfCreateRoleBindingCheckCmd(q *qliksense.Qliksense) *cobra.Command {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
return nil return nil
} }
if err = qp.CheckCreateRoleBinding(namespace); err != nil { if err = qp.CheckCreateRoleBinding(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight rolebinding check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight rolebinding check FAILED"))
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
return nil return nil
@@ -332,7 +332,7 @@ func pfCreateServiceAccountCheckCmd(q *qliksense.Qliksense) *cobra.Command {
var preflightServiceAccountCmd = &cobra.Command{ var preflightServiceAccountCmd = &cobra.Command{
Use: "serviceaccount", Use: "serviceaccount",
Short: "preflight create ServiceAccount check", Short: "preflight create serviceaccount check",
Long: `perform preflight serviceaccount check to ensure we are able to create a service account in the cluster`, Long: `perform preflight serviceaccount check to ensure we are able to create a service account in the cluster`,
Example: `qliksense preflight serviceaccount`, Example: `qliksense preflight serviceaccount`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
@@ -345,7 +345,7 @@ func pfCreateServiceAccountCheckCmd(q *qliksense.Qliksense) *cobra.Command {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
return nil return nil
} }
if err = qp.CheckCreateServiceAccount(namespace); err != nil { if err = qp.CheckCreateServiceAccount(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight ServiceAccount check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight ServiceAccount check FAILED"))
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
return nil return nil
@@ -417,7 +417,7 @@ func pfMongoCheckCmd(q *qliksense.Qliksense) *cobra.Command {
if namespace == "" { if namespace == "" {
namespace = "default" namespace = "default"
} }
if err = qp.CheckMongo(kubeConfigContents, namespace, preflightOpts); err != nil { if err = qp.CheckMongo(kubeConfigContents, namespace, preflightOpts, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight mongo check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight mongo check FAILED"))
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
return nil return nil
@@ -429,10 +429,45 @@ func pfMongoCheckCmd(q *qliksense.Qliksense) *cobra.Command {
f := preflightMongoCmd.Flags() f := preflightMongoCmd.Flags()
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode") f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
f.StringVarP(&preflightOpts.MongoOptions.MongodbUrl, "url", "", "", "mongodbUrl to try connecting to") f.StringVarP(&preflightOpts.MongoOptions.MongodbUrl, "url", "", "", "mongodbUrl to try connecting to")
f.StringVarP(&preflightOpts.MongoOptions.Username, "username", "", "", "username to connect to mongodb")
f.StringVarP(&preflightOpts.MongoOptions.Password, "password", "", "", "password to connect to mongodb")
f.StringVarP(&preflightOpts.MongoOptions.CaCertFile, "ca-cert", "", "", "ca certificate to use for mongodb check") f.StringVarP(&preflightOpts.MongoOptions.CaCertFile, "ca-cert", "", "", "ca certificate to use for mongodb check")
f.StringVarP(&preflightOpts.MongoOptions.ClientCertFile, "client-cert", "", "", "client-certificate to use for mongodb check")
f.BoolVar(&preflightOpts.MongoOptions.Tls, "tls", false, "enable tls?")
return preflightMongoCmd return preflightMongoCmd
} }
func pfCleanupCmd(q *qliksense.Qliksense) *cobra.Command {
out := ansi.NewColorableStdout()
preflightOpts := &preflight.PreflightOptions{
MongoOptions: &preflight.MongoOptions{},
}
var pfCleanCmd = &cobra.Command{
Use: "clean",
Short: "perform preflight clean",
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}
// Preflight clean
namespace, kubeConfigContents, err := preflight.InitPreflight()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight cleanup FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
if namespace == "" {
namespace = "default"
}
if err = qp.Cleanup(namespace, kubeConfigContents); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight cleanup FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
fmt.Fprintf(out, "%s\n", Green("Preflight cleanup complete"))
return nil
},
}
f := pfCleanCmd.Flags()
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
return pfCleanCmd
}

View File

@@ -8,6 +8,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
. "github.com/logrusorgru/aurora"
ansi "github.com/mattn/go-colorable" ansi "github.com/mattn/go-colorable"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
"github.com/qlik-oss/sense-installer/pkg" "github.com/qlik-oss/sense-installer/pkg"
@@ -15,7 +16,6 @@ import (
"github.com/qlik-oss/sense-installer/pkg/qliksense" "github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
. "github.com/logrusorgru/aurora"
) )
// To run this project in debug mode, run: // To run this project in debug mode, run:
@@ -46,7 +46,7 @@ func initAndExecute() error {
log.Fatal(err) log.Fatal(err)
} }
// create dirs and appropriate files for setting up contexts // create dirs and appropriate files for setting up contexts
api.LogDebugMessage("QliksenseHomeDir: %s", qlikSenseHome) api.LogDebugMessage("QliksenseHomeDir: %s\n", qlikSenseHome)
qliksenseClient := qliksense.New(qlikSenseHome) qliksenseClient := qliksense.New(qlikSenseHome)
cmd := rootCmd(qliksenseClient) cmd := rootCmd(qliksenseClient)
@@ -122,6 +122,7 @@ func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
globalEulaPostRun(cmd, p) globalEulaPostRun(cmd, p)
} }
}, },
SilenceUsage: true,
} }
origHelpFunc := cmd.HelpFunc() origHelpFunc := cmd.HelpFunc()
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
@@ -195,10 +196,13 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
// add clean-config-repo-patches command as a sub-command to the app config sub-command // add clean-config-repo-patches command as a sub-command to the app config sub-command
configCmd.AddCommand(cleanConfigRepoPatchesCmd(p)) configCmd.AddCommand(cleanConfigRepoPatchesCmd(p))
// open editor for config // open editor for config
configCmd.AddCommand(configEditCmd(p)) configCmd.AddCommand(configEditCmd(p))
// add unset for config
configCmd.AddCommand((unsetCmd(p)))
// add uninstall command // add uninstall command
cmd.AddCommand(uninstallCmd(p)) cmd.AddCommand(uninstallCmd(p))
@@ -220,6 +224,7 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
preflightCmd.AddCommand(pfCreateRoleBindingCheckCmd(p)) preflightCmd.AddCommand(pfCreateRoleBindingCheckCmd(p))
preflightCmd.AddCommand(pfCreateServiceAccountCheckCmd(p)) preflightCmd.AddCommand(pfCreateServiceAccountCheckCmd(p))
preflightCmd.AddCommand(pfCreateAuthCheckCmd(p)) preflightCmd.AddCommand(pfCreateAuthCheckCmd(p))
preflightCmd.AddCommand(pfCleanupCmd(p))
cmd.AddCommand(preflightCmd) cmd.AddCommand(preflightCmd)
cmd.AddCommand(loadCrFile(p)) cmd.AddCommand(loadCrFile(p))

View File

@@ -23,6 +23,12 @@ Run the following command to execute a specific check
qliksense preflight dns qliksense preflight dns
``` ```
#### Running cleanup
Run the following command to cleanup entities created for preflight checks that were left behind on the cluster.
```
qliksense preflight clean
```
### qliksense load ### qliksense load
`qliksense load` command takes input from a file or from pipe `qliksense load` command takes input from a file or from pipe
@@ -57,7 +63,7 @@ spec:
value: "yes" value: "yes"
secrets: secrets:
qliksense: qliksense:
- name: mongoDbUri - name: mongodbUri
value: mongodb://qlik-test-mongodb:27017/qliksense?ssl=false value: mongodb://qlik-test-mongodb:27017/qliksense?ssl=false
profile: docker-desktop profile: docker-desktop
rotateKeys: "yes" rotateKeys: "yes"
@@ -98,7 +104,7 @@ spec:
value: "yes" value: "yes"
secrets: secrets:
qliksense: qliksense:
- name: mongoDbUri - name: mongodbUri
value: "mongo://mongo:3307" value: "mongo://mongo:3307"
- name: messagingPassword - name: messagingPassword
valueFromKey: messagingPassword valueFromKey: messagingPassword

View File

@@ -23,7 +23,7 @@ spec:
profile: docker-desktop profile: docker-desktop
secrets: secrets:
qliksense: qliksense:
- name: mongoDbUri - name: mongodbUri
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
rotateKeys: "yes" rotateKeys: "yes"
releaseName: qlik-default releaseName: qlik-default

View File

@@ -16,19 +16,29 @@ Examples:
qliksense preflight <preflight_check_to_run> qliksense preflight <preflight_check_to_run>
Available Commands: Available Commands:
all perform all checks all perform all checks
dns perform preflight dns check authcheck preflight authcheck
k8s-version check k8s version clean perform preflight clean
deployment perform preflight deployment check
dns perform preflight dns check
kube-version check kubernetes version
mongo preflight mongo OR preflight mongo --url=<url>
pod perform preflight pod check
role preflight create role check
rolebinding preflight create rolebinding check
service perform preflight service check
serviceaccount preflight create ServiceAccount check
Flags: Flags:
-h, --help help for preflight -h, --help help for preflight
-v, --verbose verbose mode
``` ```
### DNS check ### DNS check
Run the following command to perform preflight DNS check. We setup a kubernetes deployment and try to reach it as part of establishing DNS connectivity in this check. Run the following command to perform preflight DNS check. We setup a kubernetes deployment and try to reach it as part of establishing DNS connectivity in this check.
The expected output should be similar to the one shown below. The expected output should be similar to the one shown below.
```shell ```shell
$ qliksense preflight dns $ qliksense preflight dns -v
Preflight DNS check Preflight DNS check
--------------------- ---------------------
@@ -51,7 +61,7 @@ Deleted deployment: dep-dns-preflight-check
We check the version of the target kubernetes cluster and ensure that it falls in the valid range of kubernetes versions that are supported by qliksense. We check the version of the target kubernetes cluster and ensure that it falls in the valid range of kubernetes versions that are supported by qliksense.
The command to run this check and the expected similar output are as shown below: The command to run this check and the expected similar output are as shown below:
```shell ```shell
$ qliksense preflight k8s-version $ qliksense preflight k8s-version -v
Preflight kubernetes minimum version check Preflight kubernetes minimum version check
------------------------------------------ ------------------------------------------
@@ -66,7 +76,7 @@ Completed Preflight kubernetes minimum version check
### Service check ### Service check
We use the commmand below to test if we are able to create a service in the cluster. We use the commmand below to test if we are able to create a service in the cluster.
```shell ```shell
$ qliksense preflight service $ qliksense preflight service -v
Preflight service check Preflight service check
----------------------- -----------------------
@@ -82,7 +92,7 @@ Completed preflight service check
### Deployment check ### Deployment check
We use the commmand below to test if we are able to create a deployment in the cluster. After the test exexutes, we wait until the created deployment terminates before we exit the command. We use the commmand below to test if we are able to create a deployment in the cluster. After the test exexutes, we wait until the created deployment terminates before we exit the command.
```shell ```shell
$ qliksense preflight deployment $ qliksense preflight deployment -v
Preflight deployment check Preflight deployment check
----------------------- -----------------------
@@ -97,7 +107,7 @@ Completed preflight deployment check
### Pod check ### Pod check
We use the commmand below to test if we are able to create a pod in the cluster. We use the commmand below to test if we are able to create a pod in the cluster.
```shell ```shell
$ qliksense preflight pod $ qliksense preflight pod -v
Preflight pod check Preflight pod check
-------------------- --------------------
@@ -110,61 +120,61 @@ Deleted pod: pod-pf-check
Completed preflight pod check Completed preflight pod check
``` ```
### Create-Role check ### Role check
We use the command below to test if we are able to create a role in the cluster We use the command below to test if we are able to create a role in the cluster
```shell ```shell
$ qliksense preflight create-role $ qliksense preflight role -v
Preflight create-role check Preflight role check
--------------------------- ---------------------------
Preflight create-role check: Preflight role check:
Created role: role-preflight-check Created role: role-preflight-check
Preflight create-role check: PASSED Preflight role check: PASSED
Cleaning up resources... Cleaning up resources...
Deleted role: role-preflight-check Deleted role: role-preflight-check
Completed preflight create-role check Completed preflight role check
``` ```
### Create-RoleBinding check ### RoleBinding check
We use the command below to test if we are able to create a role binding in the cluster We use the command below to test if we are able to create a role binding in the cluster
```shell ```shell
$ qliksense preflight createRoleBinding $ qliksense preflight rolebinding -v
Preflight create roleBinding check Preflight rolebinding check
--------------------------- ---------------------------
Preflight createRoleBinding check: Preflight rolebinding check:
Created RoleBinding: role-binding-preflight-check Created RoleBinding: role-binding-preflight-check
Preflight createRoleBinding check: PASSED Preflight rolebinding check: PASSED
Cleaning up resources... Cleaning up resources...
Deleting RoleBinding: role-binding-preflight-check Deleting RoleBinding: role-binding-preflight-check
Deleted RoleBinding: role-binding-preflight-check Deleted RoleBinding: role-binding-preflight-check
Completed preflight createRoleBinding check Completed preflight rolebinding check
``` ```
### Create-ServiceAccount check ### Create-ServiceAccount check
We use the command below to test if we are able to create a service account in the cluster We use the command below to test if we are able to create a service account in the cluster
```shell ```shell
$ qliksense preflight createServiceAccount $ qliksense preflight serviceaccount -v
Preflight create ServiceAccount check Preflight ServiceAccount check
------------------------------------- -------------------------------------
Preflight createServiceAccount check: Preflight serviceaccount check:
Created Service Account: preflight-check-test-serviceaccount Created Service Account: preflight-check-test-serviceaccount
Preflight createServiceAccount check: PASSED Preflight serviceaccount check: PASSED
Cleaning up resources... Cleaning up resources...
Deleting ServiceAccount: preflight-check-test-serviceaccount Deleting ServiceAccount: preflight-check-test-serviceaccount
Deleted ServiceAccount: preflight-check-test-serviceaccount Deleted ServiceAccount: preflight-check-test-serviceaccount
Completed preflight createServiceAccount check Completed preflight serviceaccount check
``` ```
### CreateRB check ### Auth check
We use the command below to combine creation of role, role binding, and service account tests We use the command below to combine creation of role, role binding, and service account tests
```shell ```shell
$ qliksense preflight createRB $ qliksense preflight authcheck -v
Preflight createRB check Preflight auth check
------------------------------------- -------------------------------------
Preflight create-role check: Preflight create-role check:
Created role: role-preflight-check Created role: role-preflight-check
@@ -189,18 +199,18 @@ Cleaning up resources...
Deleted ServiceAccount: preflight-check-test-serviceaccount Deleted ServiceAccount: preflight-check-test-serviceaccount
Completed preflight createServiceAccount check Completed preflight createServiceAccount check
Completed preflight CreateRB check Completed preflight auth check
``` ```
### Mongodb check ### Mongodb check
We can check if we are able to connect to an instance of mongodb on the cluster by either supplying the mongodbUri as part of the command or infer it from the current context. We can check if we are able to connect to an instance of mongodb on the cluster by either supplying the mongodbUri as part of the command or infer it from the current context.
```shell ```shell
qliksense preflight mongo --url=<url> OR qliksense preflight mongo --url=<url> -v OR
qliksense preflight mongo qliksense preflight mongo -v
qliksense preflight mongo --url=<mongo-server url> --ca-cert=<path to ca-cert file> qliksense preflight mongo --url=<mongo-server url> --ca-cert=<path to ca-cert file> -v
```
```shell
Preflight mongo check Preflight mongo check
--------------------- ---------------------
Preflight mongodb check: Preflight mongodb check:
@@ -216,13 +226,35 @@ Deleted pod: pf-mongo-pod
Completed preflight mongodb check Completed preflight mongodb check
``` ```
#### Mongodb check with mutual tls
In order to perform mutual tls with mongo we need to:
- append client certificate to the beginning/end of CA certificate. Make sure to include the beginning and end tags on each certificate.
The CA certificate file should look like this in the end:
```shell
<existing contents of CA cert>
...
-----BEGIN RSA PRIVATE KEY-----
<private key>
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
<public key>
-----END CERTIFICATE-----
```
- Run the command below to set the ca certificate into the CR
```shell
cat <path_to_ca.crt> | base64 | qliksense config set-secrets qliksense.caCertificates --base64
```
Next, run:
```shell
qliksense preflight mongo -v
```
### Running all checks ### Running all checks
Run the command shown below to execute all preflight checks. Run the command shown below to execute all preflight checks.
```shell ```shell
$ qliksense preflight all --mongodb-url=<url> OR $ qliksense preflight all --mongodb-url=<url> -v OR
$ qliksense preflight all --mongodb-url=<mongo-server url> --mongodb-ca-cert=<path to ca-cert file> $ qliksense preflight all --mongodb-url=<mongo-server url> --mongodb-ca-cert=<path to ca-cert file> -v
Running all preflight checks Running all preflight checks
@@ -253,4 +285,23 @@ Completed Preflight kubernetes minimum version check
All preflight checks have PASSED All preflight checks have PASSED
Completed running all preflight checks Completed running all preflight checks
``` ```
### Clean
Run the command below to cleanup entities that were created for the purpose of running preflight checks and left behind in the cluster.
```shell
$ qliksense preflight clean -v
Preflight clean
----------------
Removing deployment...
Removing service...
Removing pod...
Removing role...
Removing rolebinding...
Removing serviceaccount...
Removing DNS check components...
Removing mongo check components...
Preflight cleanup complete
```

9
go.mod
View File

@@ -10,13 +10,13 @@ replace (
k8s.io/client-go => k8s.io/client-go v0.17.0 k8s.io/client-go => k8s.io/client-go v0.17.0
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200424070349-b0312eb71568 sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200514233516-4ac83864b7bd
) )
require ( require (
cloud.google.com/go v0.52.0 // indirect cloud.google.com/go v0.52.0 // indirect
cloud.google.com/go/storage v1.5.0 // indirect cloud.google.com/go/storage v1.5.0 // indirect
github.com/Masterminds/semver/v3 v3.0.3 github.com/Masterminds/semver/v3 v3.1.0
github.com/Shopify/ejson v1.2.1 github.com/Shopify/ejson v1.2.1
github.com/aws/aws-sdk-go v1.28.9 // indirect github.com/aws/aws-sdk-go v1.28.9 // indirect
github.com/bugsnag/bugsnag-go v1.5.3 // indirect github.com/bugsnag/bugsnag-go v1.5.3 // indirect
@@ -34,15 +34,13 @@ require (
github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381
github.com/mattn/go-colorable v0.1.4 github.com/mattn/go-colorable v0.1.4
github.com/mattn/go-tty v0.0.3 github.com/mattn/go-tty v0.0.3
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/otiai10/copy v1.1.1 github.com/otiai10/copy v1.1.1
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/qlik-oss/k-apis v0.1.1 github.com/qlik-oss/k-apis v0.1.4
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/rogpeppe/go-internal v1.5.2 // indirect github.com/rogpeppe/go-internal v1.5.2 // indirect
github.com/spf13/cobra v0.0.6 github.com/spf13/cobra v0.0.6
@@ -53,7 +51,6 @@ require (
golang.org/x/tools v0.0.0-20200312194400-c312e98713c2 // indirect golang.org/x/tools v0.0.0-20200312194400-c312e98713c2 // indirect
google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b // indirect google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b // indirect
gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v3 v3.0.0-20190924164351-c8b7dadae555
k8s.io/api v0.17.2 k8s.io/api v0.17.2
k8s.io/apimachinery v0.17.2 k8s.io/apimachinery v0.17.2
k8s.io/client-go v11.0.0+incompatible k8s.io/client-go v11.0.0+incompatible

12
go.sum
View File

@@ -72,6 +72,8 @@ github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RP
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14= github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14=
github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8= github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8=
github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU= github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU=
github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
@@ -881,10 +883,12 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/qlik-oss/k-apis v0.1.1 h1:aZ4eTMB3mSn03Kuj7+RI0eFLkjK9+0qxADBioRb3qVA= github.com/qlik-oss/k-apis v0.1.2 h1:BBcrXl+NxdsvuRsZuJbvIFxMv5QIXqWBzhXOcr5KUX8=
github.com/qlik-oss/k-apis v0.1.1/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U= github.com/qlik-oss/k-apis v0.1.2/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200424070349-b0312eb71568 h1:wHOUCGfnmgYqW3aCjuP3fXmB2T/uZXMvltO+F3us83E= github.com/qlik-oss/k-apis v0.1.4 h1:YXnjKXm/yhPblzYYyVCtD0dNbIkLPLlDdBKnjeYW0IY=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200424070349-b0312eb71568/go.mod h1:Yg8bqX8Mq/eSgXfcenxCxhZuSXg+NCsKq6NBdch/oUc= github.com/qlik-oss/k-apis v0.1.4/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200514233516-4ac83864b7bd h1:dYd6duTr54L7OqykGkd3Z+336frAvzsibWNYruYkYVc=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200514233516-4ac83864b7bd/go.mod h1:zh3yFgE5zFk1kreqzVyyj1eXyIxQJT53l4zSg8Wt4SA=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ= github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q= github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=

View File

@@ -157,6 +157,8 @@ func (cr *QliksenseCR) GetFetchAccessToken(encryptionKey string) string {
if tok, err := cr.Spec.FetchSource.GetAccessToken(); err != nil { if tok, err := cr.Spec.FetchSource.GetAccessToken(); err != nil {
fmt.Println(err) fmt.Println(err)
return "" return ""
} else if tok == "" {
return tok
} else { } else {
by, _ := b64.StdEncoding.DecodeString(tok) by, _ := b64.StdEncoding.DecodeString(tok)
res, err := DecryptData(by, encryptionKey) res, err := DecryptData(by, encryptionKey)

View File

@@ -107,7 +107,7 @@ func TestGetDecryptedCr(t *testing.T) {
key, _ := setupGenerateKey(dir) key, _ := setupGenerateKey(dir)
ecn, _ := EncryptData([]byte("mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"), key) ecn, _ := EncryptData([]byte("mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"), key)
b := b64.StdEncoding.EncodeToString(ecn) b := b64.StdEncoding.EncodeToString(ecn)
qcr.Spec.AddToSecrets("qliksense", "mongoDbUri", b, "") qcr.Spec.AddToSecrets("qliksense", "mongodbUri", b, "")
qcr.SetFetchAccessToken("mytoken", key) qcr.SetFetchAccessToken("mytoken", key)
@@ -117,8 +117,8 @@ func TestGetDecryptedCr(t *testing.T) {
t.Log(err) t.Log(err)
} }
decryptedValue := newCr.Spec.GetFromSecrets("qliksense", "mongoDbUri") decryptedValue := newCr.Spec.GetFromSecrets("qliksense", "mongodbUri")
orignalValue := qcr.Spec.GetFromSecrets("qliksense", "mongoDbUri") orignalValue := qcr.Spec.GetFromSecrets("qliksense", "mongodbUri")
if decryptedValue != "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false" { if decryptedValue != "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false" {
t.Fail() t.Fail()
b, _ := K8sToYaml(newCr) b, _ := K8sToYaml(newCr)

View File

@@ -24,8 +24,8 @@ const (
QliksenseDefaultProfile = "docker-desktop" QliksenseDefaultProfile = "docker-desktop"
DefaultRotateKeys = "yes" DefaultRotateKeys = "yes"
QliksenseMetadataName = "QliksenseConfigMetadata" QliksenseMetadataName = "QliksenseConfigMetadata"
DefaultMongoDbUri = "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false" DefaultMongodbUri = "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"
DefaultMongoDbUriKey = "mongoDbUri" DefaultMongodbUriKey = "mongodbUri"
) )
// AddCommonConfig adds common configs into CRs // AddCommonConfig adds common configs into CRs
@@ -40,7 +40,7 @@ func (qliksenseCR *QliksenseCR) AddCommonConfig(contextName string) {
Profile: QliksenseDefaultProfile, Profile: QliksenseDefaultProfile,
RotateKeys: DefaultRotateKeys, RotateKeys: DefaultRotateKeys,
} }
qliksenseCR.Spec.AddToSecrets("qliksense", DefaultMongoDbUriKey, DefaultMongoDbUri, "") qliksenseCR.Spec.AddToSecrets("qliksense", DefaultMongodbUriKey, DefaultMongodbUri, "")
} }
// AddBaseQliksenseConfigs adds configs into config.yaml // AddBaseQliksenseConfigs adds configs into config.yaml
@@ -96,7 +96,7 @@ func WriteToFile(content interface{}, targetFile string) error {
log.Println(err) log.Println(err)
return err return err
} }
LogDebugMessage("Wrote content into %s", targetFile) LogDebugMessage("Wrote content into %s\n", targetFile)
return nil return nil
} }

View File

@@ -26,8 +26,8 @@ func TestAddCommonConfig(t *testing.T) {
RotateKeys: DefaultRotateKeys, RotateKeys: DefaultRotateKeys,
Secrets: map[string]config.NameValues{ Secrets: map[string]config.NameValues{
"qliksense": []config.NameValue{{ "qliksense": []config.NameValue{{
Name: DefaultMongoDbUriKey, Name: DefaultMongodbUriKey,
Value: DefaultMongoDbUri, Value: DefaultMongodbUri,
}, },
}, },
}, },

View File

@@ -7,7 +7,7 @@ import (
"reflect" "reflect"
"testing" "testing"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v2"
) )
func TestDockerConfigJsonSecret(t *testing.T) { func TestDockerConfigJsonSecret(t *testing.T) {
@@ -34,10 +34,10 @@ func TestDockerConfigJsonSecret(t *testing.T) {
t.Fatalf("error unmarshalling yaml string: %v, error: %v", string(dockerConfigJsonSecretYamlBytes), err) t.Fatalf("error unmarshalling yaml string: %v, error: %v", string(dockerConfigJsonSecretYamlBytes), err)
} else if validYamlMap["apiVersion"] != "v1" || } else if validYamlMap["apiVersion"] != "v1" ||
validYamlMap["kind"] != "Secret" || validYamlMap["kind"] != "Secret" ||
validYamlMap["metadata"].(map[string]interface{})["name"] != dockerConfigJsonSecret.Name || validYamlMap["metadata"].(map[interface {}]interface {})["name"] != dockerConfigJsonSecret.Name ||
validYamlMap["type"] != "kubernetes.io/dockerconfigjson" { validYamlMap["type"] != "kubernetes.io/dockerconfigjson" {
t.Fatalf("error verifying validity of secret yaml: %v", string(dockerConfigJsonSecretYamlBytes)) t.Fatalf("error verifying validity of secret yaml: %v", string(dockerConfigJsonSecretYamlBytes))
} else if dockerConfigJsonBytesBase64, ok := validYamlMap["data"].(map[string]interface{})[".dockerconfigjson"]; !ok { } else if dockerConfigJsonBytesBase64, ok := validYamlMap["data"].(map[interface {}]interface {})[".dockerconfigjson"]; !ok {
t.Fatalf("no .dockerconfigjson data key in the secret yaml: %v", string(dockerConfigJsonSecretYamlBytes)) t.Fatalf("no .dockerconfigjson data key in the secret yaml: %v", string(dockerConfigJsonSecretYamlBytes))
} else if dockerConfigJsonEncryptedBytes, err := base64.StdEncoding.DecodeString(dockerConfigJsonBytesBase64.(string)); err != nil { } else if dockerConfigJsonEncryptedBytes, err := base64.StdEncoding.DecodeString(dockerConfigJsonBytesBase64.(string)); err != nil {
t.Fatalf("error decoding dockerConfigJsonBytes from base64: %v", err) t.Fatalf("error decoding dockerConfigJsonBytes from base64: %v", err)
@@ -45,14 +45,14 @@ func TestDockerConfigJsonSecret(t *testing.T) {
t.Fatalf("error decrypting dockerConfigJsonBytes: %v", err) t.Fatalf("error decrypting dockerConfigJsonBytes: %v", err)
} else if err := json.Unmarshal(dockerConfigJsonBytes, &dockerConfigJsonMap); err != nil { } else if err := json.Unmarshal(dockerConfigJsonBytes, &dockerConfigJsonMap); err != nil {
t.Fatalf("error unmarshalling dockerConfigJson from json: %v", err) t.Fatalf("error unmarshalling dockerConfigJson from json: %v", err)
} else if dockerConfigJson, ok := dockerConfigJsonMap["auths"].(map[string]interface{})[dockerConfigJsonSecret.Uri]; !ok { } else if dockerConfigJson, ok := dockerConfigJsonMap["auths"].(map[string]interface {})[dockerConfigJsonSecret.Uri]; !ok {
t.Fatalf("dockerConfigJson map does not contain data for the registry: %v", dockerConfigJsonSecret.Uri) t.Fatalf("dockerConfigJson map does not contain data for the registry: %v", dockerConfigJsonSecret.Uri)
} else if dockerConfigJson.(map[string]interface{})["username"] != dockerConfigJsonSecret.Username || } else if dockerConfigJson.(map[string]interface {})["username"] != dockerConfigJsonSecret.Username ||
dockerConfigJson.(map[string]interface{})["password"] != dockerConfigJsonSecret.Password || dockerConfigJson.(map[string]interface {})["password"] != dockerConfigJsonSecret.Password ||
dockerConfigJson.(map[string]interface{})["email"] != dockerConfigJsonSecret.Email { dockerConfigJson.(map[string]interface {})["email"] != dockerConfigJsonSecret.Email {
t.Fatal("dockerConfigJson map does not contain expected values") t.Fatal("dockerConfigJson map does not contain expected values")
} else { } else {
authBase64 := dockerConfigJson.(map[string]interface{})["auth"] authBase64 := dockerConfigJson.(map[string]interface {})["auth"]
if auth, err := base64.StdEncoding.DecodeString(authBase64.(string)); err != nil { if auth, err := base64.StdEncoding.DecodeString(authBase64.(string)); err != nil {
t.Fatal("error base64 decoding auth value") t.Fatal("error base64 decoding auth value")
} else if string(auth) != fmt.Sprintf("%s:%s", dockerConfigJsonSecret.Username, dockerConfigJsonSecret.Password) { } else if string(auth) != fmt.Sprintf("%s:%s", dockerConfigJsonSecret.Username, dockerConfigJsonSecret.Password) {

View File

@@ -17,8 +17,9 @@ type PreflightConfig struct {
} }
type PreflightSpec struct { type PreflightSpec struct {
MinK8sVersion string `json:"minK8sVersion,omitempty" yaml:"minK8sVersion,omitempty"` MinK8sVersion string `json:"minK8sVersion,omitempty" yaml:"minK8sVersion,omitempty"`
Images map[string]string `json:"images,omitempty" yaml:"images,omitempty"` MinMongoVersion string `json:"minMongoVersion,omitempty" yaml:"minMongoVersion,omitempty"`
Images map[string]string `json:"images,omitempty" yaml:"images,omitempty"`
} }
//NewPreflightConfigEmpty create empty PreflightConfig object //NewPreflightConfigEmpty create empty PreflightConfig object
@@ -74,6 +75,13 @@ func (p *PreflightConfig) AddMinK8sV(version string) {
p.Spec.MinK8sVersion = version p.Spec.MinK8sVersion = version
} }
func (p *PreflightConfig) AddMinMongoV(version string) {
if p.Spec == nil {
p.Spec = &PreflightSpec{}
}
p.Spec.MinMongoVersion = version
}
func (p *PreflightConfig) AddImage(imageFor, imageName string) { func (p *PreflightConfig) AddImage(imageFor, imageName string) {
if p.Spec.Images == nil { if p.Spec.Images == nil {
p.Spec.Images = make(map[string]string) p.Spec.Images = make(map[string]string)
@@ -101,6 +109,11 @@ func (p *PreflightConfig) GetImageName(imageFor string, accountForImageRegistry
func (p *PreflightConfig) GetMinK8sVersion() string { func (p *PreflightConfig) GetMinK8sVersion() string {
return p.Spec.MinK8sVersion return p.Spec.MinK8sVersion
} }
func (p *PreflightConfig) GetMinMongoVersion() string {
return p.Spec.MinMongoVersion
}
func (p *PreflightConfig) IsExistOnDisk() bool { func (p *PreflightConfig) IsExistOnDisk() bool {
if _, err := os.Lstat(p.GetConfigFilePath()); err != nil { if _, err := os.Lstat(p.GetConfigFilePath()); err != nil {
return false return false
@@ -117,8 +130,9 @@ func (p *PreflightConfig) Initialize() error {
return nil return nil
} }
p.AddMinK8sV("1.15") p.AddMinK8sV("1.15")
p.AddMinMongoV("3.6")
p.AddImage("nginx", "nginx") p.AddImage("nginx", "nginx")
p.AddImage("netcat", "subfuzion/netcat") p.AddImage("netcat", "subfuzion/netcat")
p.AddImage("mongo", "mongo") p.AddImage("preflight-mongo", "qlik-docker-oss.bintray.io/preflight-mongo")
return p.Write() return p.Write()
} }

View File

@@ -23,7 +23,7 @@ func checkExists(filename string) os.FileInfo {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil return nil
} }
LogDebugMessage("File exists") LogDebugMessage("File exists\n")
return info return info
} }
@@ -73,7 +73,7 @@ func ProcessConfigArgs(args []string, base64Encoded bool) ([]*ServiceKeyValue, e
resultSvcKV := make([]*ServiceKeyValue, len(args)) resultSvcKV := make([]*ServiceKeyValue, len(args))
// qliksense.mongodb=somethig // qliksense.mongodb=somethig
for i, arg := range args { for i, arg := range args {
LogDebugMessage("Arg received: %s", arg) LogDebugMessage("Arg received: %s\n", arg)
first := strings.SplitN(arg, "=", 2) first := strings.SplitN(arg, "=", 2)
if len(first) != 2 { if len(first) != 2 {
return nil, notValidErr return nil, notValidErr

View File

@@ -3,9 +3,9 @@ package preflight
import ( import (
"fmt" "fmt"
. "github.com/logrusorgru/aurora"
ansi "github.com/mattn/go-colorable" ansi "github.com/mattn/go-colorable"
"github.com/pkg/errors" "github.com/pkg/errors"
. "github.com/logrusorgru/aurora"
) )
func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions) error { func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions) error {
@@ -24,7 +24,7 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
totalCount++ totalCount++
// Preflight deployment check // Preflight deployment check
if err := qp.CheckDeployment(namespace, kubeConfigContents); err != nil { if err := qp.CheckDeployment(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED"))
fmt.Printf("Error: %v\n\n", err) fmt.Printf("Error: %v\n\n", err)
} else { } else {
@@ -34,7 +34,7 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
totalCount++ totalCount++
// Preflight service check // Preflight service check
if err := qp.CheckService(namespace, kubeConfigContents); err != nil { if err := qp.CheckService(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED"))
fmt.Printf("Error: %v\n\n", err) fmt.Printf("Error: %v\n\n", err)
} else { } else {
@@ -44,7 +44,7 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
totalCount++ totalCount++
// Preflight pod check // Preflight pod check
if err := qp.CheckPod(namespace, kubeConfigContents); err != nil { if err := qp.CheckPod(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED"))
fmt.Printf("Error: %v\n\n", err) fmt.Printf("Error: %v\n\n", err)
} else { } else {
@@ -54,7 +54,7 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
totalCount++ totalCount++
// Preflight role check // Preflight role check
if err := qp.CheckCreateRole(namespace); err != nil { if err := qp.CheckCreateRole(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED")) fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED"))
fmt.Printf("Error: %v\n\n", err) fmt.Printf("Error: %v\n\n", err)
} else { } else {
@@ -64,7 +64,7 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
totalCount++ totalCount++
// Preflight rolebinding check // Preflight rolebinding check
if err := qp.CheckCreateRoleBinding(namespace); err != nil { if err := qp.CheckCreateRoleBinding(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red(" Preflight rolebinding check FAILED")) fmt.Fprintf(out, "%s\n", Red(" Preflight rolebinding check FAILED"))
fmt.Printf("Error: %v\n\n", err) fmt.Printf("Error: %v\n\n", err)
} else { } else {
@@ -74,7 +74,7 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
totalCount++ totalCount++
// Preflight serviceaccount check // Preflight serviceaccount check
if err := qp.CheckCreateServiceAccount(namespace); err != nil { if err := qp.CheckCreateServiceAccount(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red(" Preflight serviceaccount check FAILED")) fmt.Fprintf(out, "%s\n", Red(" Preflight serviceaccount check FAILED"))
fmt.Printf("Error: %v\n\n", err) fmt.Printf("Error: %v\n\n", err)
} else { } else {
@@ -84,7 +84,7 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
totalCount++ totalCount++
// Preflight mongo check // Preflight mongo check
if err := qp.CheckMongo(kubeConfigContents, namespace, preflightOpts); err != nil { if err := qp.CheckMongo(kubeConfigContents, namespace, preflightOpts, false); err != nil {
fmt.Fprintf(out, "%s\n", Red(" Preflight mongo check FAILED")) fmt.Fprintf(out, "%s\n", Red(" Preflight mongo check FAILED"))
fmt.Printf("Error: %v\n\n", err) fmt.Printf("Error: %v\n\n", err)
} else { } else {
@@ -94,7 +94,7 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
totalCount++ totalCount++
// Preflight DNS check // Preflight DNS check
if err := qp.CheckDns(namespace, kubeConfigContents); err != nil { if err := qp.CheckDns(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red(" Preflight DNS check FAILED")) fmt.Fprintf(out, "%s\n", Red(" Preflight DNS check FAILED"))
fmt.Printf("Error: %v\n\n", err) fmt.Printf("Error: %v\n\n", err)
} else { } else {

View File

@@ -6,7 +6,7 @@ import (
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
) )
func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigContents []byte) error { func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigContents []byte, cleanup bool) error {
clientset, _, err := getK8SClientSet(kubeConfigContents, "") clientset, _, err := getK8SClientSet(kubeConfigContents, "")
if err != nil { if err != nil {
err = fmt.Errorf("Kube config error: %v\n", err) err = fmt.Errorf("Kube config error: %v\n", err)
@@ -14,63 +14,80 @@ func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigConten
} }
// Deployment check // Deployment check
qp.P.LogVerboseMessage("Preflight deployment check: \n") if !cleanup {
qp.P.LogVerboseMessage("--------------------------- \n") qp.P.LogVerboseMessage("Preflight deployment check: \n")
err = qp.checkPfDeployment(clientset, namespace, "deployment-preflight-check") qp.P.LogVerboseMessage("--------------------------- \n")
}
err = qp.checkPfDeployment(clientset, namespace, cleanup)
if err != nil { if err != nil {
qp.P.LogVerboseMessage("Preflight Deployment check: FAILED\n") qp.P.LogVerboseMessage("Preflight Deployment check: FAILED\n")
return err return err
} }
qp.P.LogVerboseMessage("Completed preflight deployment check\n") if !cleanup {
qp.P.LogVerboseMessage("Completed preflight deployment check\n")
}
return nil return nil
} }
func (qp *QliksensePreflight) CheckService(namespace string, kubeConfigContents []byte) error { func (qp *QliksensePreflight) CheckService(namespace string, kubeConfigContents []byte, cleanup bool) error {
clientset, _, err := getK8SClientSet(kubeConfigContents, "") clientset, _, err := getK8SClientSet(kubeConfigContents, "")
if err != nil { if err != nil {
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err) err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
return err return err
} }
// Service check // Service check
qp.P.LogVerboseMessage("Preflight service check: \n") if !cleanup {
qp.P.LogVerboseMessage("------------------------ \n") qp.P.LogVerboseMessage("Preflight service check: \n")
err = qp.checkPfService(clientset, namespace) qp.P.LogVerboseMessage("------------------------ \n")
}
err = qp.checkPfService(clientset, namespace, cleanup)
if err != nil { if err != nil {
qp.P.LogVerboseMessage("Preflight Service check: FAILED\n") qp.P.LogVerboseMessage("Preflight Service check: FAILED\n")
return err return err
} }
qp.P.LogVerboseMessage("Completed preflight service check\n")
if !cleanup {
qp.P.LogVerboseMessage("Completed preflight service check\n")
}
return nil return nil
} }
func (qp *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []byte) error { func (qp *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []byte, cleanup bool) error {
clientset, _, err := getK8SClientSet(kubeConfigContents, "") clientset, _, err := getK8SClientSet(kubeConfigContents, "")
if err != nil { if err != nil {
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err) err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
return err return err
} }
// Pod check // Pod check
qp.P.LogVerboseMessage("Preflight pod check: \n") if !cleanup {
qp.P.LogVerboseMessage("-------------------- \n") qp.P.LogVerboseMessage("Preflight pod check: \n")
err = qp.checkPfPod(clientset, namespace) qp.P.LogVerboseMessage("-------------------- \n")
}
err = qp.checkPfPod(clientset, namespace, cleanup)
if err != nil { if err != nil {
qp.P.LogVerboseMessage("Preflight Pod check: FAILED\n") qp.P.LogVerboseMessage("Preflight Pod check: FAILED\n")
return err return err
} }
qp.P.LogVerboseMessage("Completed preflight pod check\n") if !cleanup {
qp.P.LogVerboseMessage("Completed preflight pod check\n")
}
return nil return nil
} }
func (qp *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namespace string) error { func (qp *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namespace string, cleanup bool) error {
// create a pod // delete the pod we are going to create, if it already exists in the cluster
podName := "pod-pf-check" podName := "pod-pf-check"
qp.deletePod(clientset, namespace, podName)
if cleanup {
return nil
}
commandToRun := []string{} commandToRun := []string{}
imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true) imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
if err != nil { if err != nil {
return err return err
} }
// create a pod
pod, err := qp.createPreflightTestPod(clientset, namespace, podName, imageName, nil, commandToRun) pod, err := qp.createPreflightTestPod(clientset, namespace, podName, imageName, nil, commandToRun)
if err != nil { if err != nil {
err = fmt.Errorf("unable to create pod - %v\n", err) err = fmt.Errorf("unable to create pod - %v\n", err)
@@ -87,9 +104,14 @@ func (qp *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namesp
return nil return nil
} }
func (qp *QliksensePreflight) checkPfService(clientset *kubernetes.Clientset, namespace string) error { func (qp *QliksensePreflight) checkPfService(clientset *kubernetes.Clientset, namespace string, cleanup bool) error {
// creating service // delete the service we are going to create, if it already exists in the cluster
serviceName := "svc-pf-check" serviceName := "svc-pf-check"
qp.deleteService(clientset, namespace, serviceName)
if cleanup {
return nil
}
// creating service
pfService, err := qp.createPreflightTestService(clientset, namespace, serviceName) pfService, err := qp.createPreflightTestService(clientset, namespace, serviceName)
if err != nil { if err != nil {
err = fmt.Errorf("unable to create service - %v\n", err) err = fmt.Errorf("unable to create service - %v\n", err)
@@ -106,7 +128,14 @@ func (qp *QliksensePreflight) checkPfService(clientset *kubernetes.Clientset, na
return nil return nil
} }
func (qp *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset, namespace, depName string) error { func (qp *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset, namespace string, cleanup bool) error {
// delete the deployment we are going to create, if it already exists in the cluster
depName := "deployment-preflight-check"
qp.deleteDeployment(clientset, namespace, depName)
if cleanup {
return nil
}
// check if we are able to create a deployment // check if we are able to create a deployment
imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true) imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
if err != nil { if err != nil {

View File

@@ -3,6 +3,8 @@ package preflight
import ( import (
"fmt" "fmt"
"strings" "strings"
"k8s.io/client-go/kubernetes"
) )
const ( const (
@@ -10,21 +12,32 @@ const (
netcat = "netcat" netcat = "netcat"
) )
func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byte) error { func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byte, cleanup bool) error {
qp.P.LogVerboseMessage("Preflight DNS check: \n") depName := "dep-dns-preflight-check"
qp.P.LogVerboseMessage("------------------- \n") serviceName := "svc-dns-pf-check"
podName := "pf-pod-1"
if !cleanup {
qp.P.LogVerboseMessage("Preflight DNS check: \n")
qp.P.LogVerboseMessage("------------------- \n")
}
clientset, _, err := getK8SClientSet(kubeConfigContents, "") clientset, _, err := getK8SClientSet(kubeConfigContents, "")
if err != nil { if err != nil {
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err) err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
return 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)
if cleanup {
return nil
}
// creating deployment // creating deployment
depName := "dep-dns-preflight-check"
nginxImageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true) nginxImageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
if err != nil { if err != nil {
return err return err
} }
dnsDeployment, err := qp.createPreflightTestDeployment(clientset, namespace, depName, nginxImageName) dnsDeployment, err := qp.createPreflightTestDeployment(clientset, namespace, depName, nginxImageName)
if err != nil { if err != nil {
err = fmt.Errorf("unable to create deployment: %v\n", err) err = fmt.Errorf("unable to create deployment: %v\n", err)
@@ -37,7 +50,6 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by
} }
// creating service // creating service
serviceName := "svc-dns-pf-check"
dnsService, err := qp.createPreflightTestService(clientset, namespace, serviceName) dnsService, err := qp.createPreflightTestService(clientset, namespace, serviceName)
if err != nil { if err != nil {
err = fmt.Errorf("unable to create service : %s, %s\n", serviceName, err) err = fmt.Errorf("unable to create service : %s, %s\n", serviceName, err)
@@ -46,13 +58,13 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by
defer qp.deleteService(clientset, namespace, serviceName) defer qp.deleteService(clientset, namespace, serviceName)
// create a pod // create a pod
podName := "pf-pod-1"
commandToRun := []string{"sh", "-c", "sleep 10; nc -z -v -w 1 " + dnsService.Name + " 80"} commandToRun := []string{"sh", "-c", "sleep 10; nc -z -v -w 1 " + dnsService.Name + " 80"}
netcatImageName, err := qp.GetPreflightConfigObj().GetImageName(netcat, true) netcatImageName, err := qp.GetPreflightConfigObj().GetImageName(netcat, true)
if err != nil { if err != nil {
err = fmt.Errorf("unable to retrieve image : %v\n", err) err = fmt.Errorf("unable to retrieve image : %v\n", err)
return err return err
} }
dnsPod, err := qp.createPreflightTestPod(clientset, namespace, podName, netcatImageName, nil, commandToRun) dnsPod, err := qp.createPreflightTestPod(clientset, namespace, podName, netcatImageName, nil, commandToRun)
if err != nil { if err != nil {
err = fmt.Errorf("unable to create pod : %s, %s\n", podName, err) err = fmt.Errorf("unable to create pod : %s, %s\n", podName, err)
@@ -83,9 +95,16 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by
err = fmt.Errorf("Expected response not found\n") err = fmt.Errorf("Expected response not found\n")
return err return err
} }
if !cleanup {
qp.P.LogVerboseMessage("Completed preflight DNS check\n") qp.P.LogVerboseMessage("Completed preflight DNS check\n")
qp.P.LogVerboseMessage("Cleaning up resources...\n") qp.P.LogVerboseMessage("Cleaning up resources...\n")
}
return nil 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)
}

View File

@@ -3,8 +3,12 @@ package preflight
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"path/filepath"
"strings" "strings"
"github.com/Masterminds/semver/v3"
"github.com/pkg/errors"
"github.com/qlik-oss/sense-installer/pkg/api" "github.com/qlik-oss/sense-installer/pkg/api"
qapi "github.com/qlik-oss/sense-installer/pkg/api" qapi "github.com/qlik-oss/sense-installer/pkg/api"
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
@@ -12,113 +16,108 @@ import (
) )
const ( const (
mongo = "mongo" preflight_mongo = "preflight-mongo"
caCertMountPath = "/etc/ssl/certs/ca-certificates.crt"
) )
func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions) error { func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error {
qp.P.LogVerboseMessage("Preflight mongodb check: \n") if !cleanup {
qp.P.LogVerboseMessage("------------------------ \n") qp.P.LogVerboseMessage("Preflight mongodb check: \n")
qp.P.LogVerboseMessage("------------------------ \n")
if preflightOpts.MongoOptions.MongodbUrl == "" {
// infer mongoDbUrl from currentCR
qp.P.LogVerboseMessage("MongoDbUri is empty, infer from CR\n")
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
var currentCR *qapi.QliksenseCR
var err error
qConfig.SetNamespace(namespace)
currentCR, err = qConfig.GetCurrentCR()
if err != nil {
qp.P.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)
return err
}
preflightOpts.MongoOptions.MongodbUrl = decryptedCR.Spec.GetFromSecrets("qliksense", "mongoDbUri")
} }
var currentCR *qapi.QliksenseCR
qp.P.LogVerboseMessage("MongodbUrl: %s\n", preflightOpts.MongoOptions.MongodbUrl) var err error
if err := qp.mongoConnCheck(kubeConfigContents, namespace, preflightOpts); err != nil { qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
qConfig.SetNamespace(namespace)
currentCR, err = qConfig.GetCurrentCR()
if err != nil {
qp.P.LogVerboseMessage("Unable to retrieve current CR: %v\n", err)
return err return err
} }
qp.P.LogVerboseMessage("Completed preflight mongodb check\n") decryptedCR, err := qConfig.GetDecryptedCr(currentCR)
if err != nil {
qp.P.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")
preflightOpts.MongoOptions.MongodbUrl = strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("qliksense", "mongodbUri"))
}
if preflightOpts.MongoOptions.CaCertFile == "" && !cleanup {
caCertStr := decryptedCR.Spec.GetFromSecrets("qliksense", "caCertificates")
tmpDir := os.TempDir()
caCrtFile := filepath.Join(tmpDir, "rootCA.crt")
api.LogDebugMessage("received ca crt: %s\n", caCertStr)
if err := ioutil.WriteFile(caCrtFile, []byte(caCertStr), 0644); err != nil {
return fmt.Errorf("unable to write CA crt to file: %v", err)
}
preflightOpts.MongoOptions.CaCertFile = caCrtFile
}
if !cleanup {
qp.P.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")
return errors.New("MongoDbUrl is empty")
}
}
if err := qp.mongoConnCheck(kubeConfigContents, namespace, preflightOpts, cleanup); err != nil {
return err
}
if !cleanup {
qp.P.LogVerboseMessage("Completed preflight mongodb check\n")
}
return nil return nil
} }
func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions) error { func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error {
var caCertSecretName, clientCertSecretName string caCertSecretName := "ca-certificates-crt"
mongoPodName := "pf-mongo-pod"
clientset, _, err := getK8SClientSet(kubeConfigContents, "") clientset, _, err := getK8SClientSet(kubeConfigContents, "")
if err != nil { if err != nil {
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err) err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
return err return err
} }
var secrets []string
// cleanup before starting check
qp.runMongoCleanup(clientset, namespace, mongoPodName, caCertSecretName)
if cleanup {
return nil
}
secrets := map[string]string{}
if preflightOpts.MongoOptions.CaCertFile != "" { if preflightOpts.MongoOptions.CaCertFile != "" {
caCertSecretName = "preflight-mongo-test-cacert"
caCertSecret, err := qp.createSecret(clientset, namespace, preflightOpts.MongoOptions.CaCertFile, caCertSecretName) caCertSecret, err := qp.createSecret(clientset, namespace, preflightOpts.MongoOptions.CaCertFile, caCertSecretName)
if err != nil { if err != nil {
err = fmt.Errorf("unable to create a ca cert kubernetes secret: %v\n", err) err = fmt.Errorf("unable to create a ca cert kubernetes secret: %v\n", err)
return err return err
} }
defer qp.deleteK8sSecret(clientset, namespace, caCertSecret) defer qp.deleteK8sSecret(clientset, namespace, caCertSecret.Name)
secrets = append(secrets, caCertSecretName) secrets[caCertSecretName] = caCertMountPath
}
if preflightOpts.MongoOptions.ClientCertFile != "" {
clientCertSecretName = "preflight-mongo-test-clientcert"
clientCertSecret, err := qp.createSecret(clientset, namespace, preflightOpts.MongoOptions.ClientCertFile, clientCertSecretName)
if err != nil {
err = fmt.Errorf("unable to create a client cert kubernetes secret: %v\n", err)
return err
}
defer qp.deleteK8sSecret(clientset, namespace, clientCertSecret)
secrets = append(secrets, clientCertSecretName)
} }
mongoCommand := strings.Builder{} commandToRun := []string{"./preflight-mongo", fmt.Sprintf(`-url="%s"`, preflightOpts.MongoOptions.MongodbUrl)}
mongoCommand.WriteString(fmt.Sprintf("sleep 10;mongo %s", preflightOpts.MongoOptions.MongodbUrl))
if preflightOpts.MongoOptions.Username != "" {
mongoCommand.WriteString(fmt.Sprintf(" --username %s", preflightOpts.MongoOptions.Username))
api.LogDebugMessage("Adding username: Mongo command: %s\n", mongoCommand.String())
}
if preflightOpts.MongoOptions.Password != "" {
mongoCommand.WriteString(fmt.Sprintf(" --password %s", preflightOpts.MongoOptions.Password))
api.LogDebugMessage("Adding username and password\n")
}
if preflightOpts.MongoOptions.Tls || preflightOpts.MongoOptions.CaCertFile != "" || preflightOpts.MongoOptions.ClientCertFile != "" {
mongoCommand.WriteString(" --tls")
api.LogDebugMessage("Adding --tls: Mongo command: %s\n", mongoCommand.String())
}
if preflightOpts.MongoOptions.CaCertFile != "" {
mongoCommand.WriteString(fmt.Sprintf(" --tlsCAFile=/etc/ssl/%s/%[1]s", caCertSecretName))
api.LogDebugMessage("Adding caCertFile: Mongo command: %s\n", mongoCommand.String())
}
if preflightOpts.MongoOptions.ClientCertFile != "" {
mongoCommand.WriteString(fmt.Sprintf(" --tlsCertificateKeyFile=/etc/ssl/%s/%[1]s", clientCertSecretName))
api.LogDebugMessage("Adding clientCertFile: Mongo command: %s\n", mongoCommand.String())
}
mongoCommand.WriteString(` --eval "print(\"connected to mongo\")"`)
commandToRun := []string{"sh", "-c", mongoCommand.String()}
api.LogDebugMessage("Mongo command: %s\n", strings.Join(commandToRun, " ")) api.LogDebugMessage("Mongo command: %s\n", strings.Join(commandToRun, " "))
// create a pod // create a pod
podName := "pf-mongo-pod" imageName, err := qp.GetPreflightConfigObj().GetImageName(preflight_mongo, true)
imageName, err := qp.GetPreflightConfigObj().GetImageName(mongo, true)
if err != nil { if err != nil {
err = fmt.Errorf("unable to retrieve image : %v\n", err) err = fmt.Errorf("unable to retrieve image : %v\n", err)
return err return err
} }
mongoPod, err := qp.createPreflightTestPod(clientset, namespace, podName, imageName, secrets, commandToRun) api.LogDebugMessage("image name to be used: %s\n", imageName)
mongoPod, err := qp.createPreflightTestPod(clientset, namespace, mongoPodName, imageName, secrets, commandToRun)
if err != nil { if err != nil {
err = fmt.Errorf("unable to create pod : %v\n", err) err = fmt.Errorf("unable to create pod : %v\n", err)
return err return err
} }
defer qp.deletePod(clientset, namespace, podName) defer qp.deletePod(clientset, namespace, mongoPodName)
if err := waitForPod(clientset, namespace, mongoPod); err != nil { if err := waitForPod(clientset, namespace, mongoPod); err != nil {
return err return err
@@ -134,7 +133,14 @@ func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespac
return err return err
} }
stringToCheck := "Implicit session:" // check mongo server version
ok, err := qp.checkMongoVersion(logStr)
if !ok || err != nil {
return err
}
// check if connection succeeded
stringToCheck := "qlik - connection succeeded!!"
if strings.Contains(logStr, stringToCheck) { if strings.Contains(logStr, stringToCheck) {
qp.P.LogVerboseMessage("Preflight mongo check: PASSED\n") qp.P.LogVerboseMessage("Preflight mongo check: PASSED\n")
} else { } else {
@@ -144,6 +150,43 @@ func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespac
return nil return nil
} }
func (qp *QliksensePreflight) checkMongoVersion(logStr string) (bool, error) {
// check mongo server version
api.LogDebugMessage("Minimum required mongo version: %s\n", qp.GetPreflightConfigObj().GetMinMongoVersion())
mongoVersionStrToCheck := "qlik mongo server version:"
if strings.Contains(logStr, mongoVersionStrToCheck) {
logLines := strings.Split(logStr, "\n")
for _, eachline := range logLines {
if strings.Contains(eachline, mongoVersionStrToCheck) {
mongoVersionLog := strings.Split(eachline, ":")
if len(mongoVersionLog) < 2 {
continue
}
mongoVersionStr := strings.ReplaceAll(strings.TrimSpace(mongoVersionLog[1]), `"`, "")
api.LogDebugMessage("Extracted mongo version from pod log: %s\n", mongoVersionStr)
currentMongoVersionSemver, err := semver.NewVersion(mongoVersionStr)
if err != nil {
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())
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)
return true, nil
}
err = fmt.Errorf("Current mongodb server version %s is less than minimum required mongodb version: %s", currentMongoVersionSemver, minMongoVersionSemver)
return false, err
}
}
}
err := errors.New("Unable to infer mongodb server version")
return false, err
}
func (qp *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, namespace, certFile, certSecretName string) (*apiv1.Secret, error) { func (qp *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, namespace, certFile, certSecretName string) (*apiv1.Secret, error) {
certBytes, err := ioutil.ReadFile(certFile) certBytes, err := ioutil.ReadFile(certFile)
if err != nil { if err != nil {
@@ -152,8 +195,13 @@ func (qp *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, name
certSecret, err := qp.createPreflightTestSecret(clientset, namespace, certSecretName, certBytes) certSecret, err := qp.createPreflightTestSecret(clientset, namespace, certSecretName, certBytes)
if err != nil { if err != nil {
err = fmt.Errorf("unable to create secret with ca cert : %v\n", err) err = fmt.Errorf("unable to create secret with cert : %v\n", err)
return nil, err return nil, err
} }
return certSecret, nil return certSecret, nil
} }
func (qp *QliksensePreflight) runMongoCleanup(clientset *kubernetes.Clientset, namespace, mongoPodName, caCertSecretName string) {
qp.deletePod(clientset, namespace, mongoPodName)
qp.deleteK8sSecret(clientset, namespace, caCertSecretName)
}

View File

@@ -12,7 +12,6 @@ import (
"time" "time"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
"github.com/qlik-oss/sense-installer/pkg/api" "github.com/qlik-oss/sense-installer/pkg/api"
"github.com/qlik-oss/sense-installer/pkg/qliksense" "github.com/qlik-oss/sense-installer/pkg/qliksense"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
@@ -40,12 +39,8 @@ func (p *PreflightOptions) LogVerboseMessage(strMessage string, args ...interfac
} }
type MongoOptions struct { type MongoOptions struct {
MongodbUrl string MongodbUrl string
Username string CaCertFile string
Password string
CaCertFile string
ClientCertFile string
Tls bool
} }
var gracePeriod int64 = 0 var gracePeriod int64 = 0
@@ -115,13 +110,13 @@ func getK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clients
if len(kubeconfig) == 0 { if len(kubeconfig) == 0 {
clientConfig, err = rest.InClusterConfig() clientConfig, err = rest.InClusterConfig()
if err != nil { if err != nil {
err = errors.Wrap(err, "Unable to load in-cluster kubeconfig") err = fmt.Errorf("Unable to load in-cluster kubeconfig: %w", err)
return nil, nil, err return nil, nil, err
} }
} else { } else {
config, err := clientcmd.Load(kubeconfig) config, err := clientcmd.Load(kubeconfig)
if err != nil { if err != nil {
err = errors.Wrap(err, "Unable to load kubeconfig") err = fmt.Errorf("Unable to load kubeconfig: %w", err)
return nil, nil, err return nil, nil, err
} }
if contextName != "" { if contextName != "" {
@@ -129,13 +124,13 @@ func getK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clients
} }
clientConfig, err = clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig() clientConfig, err = clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig()
if err != nil { if err != nil {
err = errors.Wrap(err, "Unable to create client config from config") err = fmt.Errorf("Unable to create client config from config: %w", err)
return nil, nil, err return nil, nil, err
} }
} }
clientset, err := kubernetes.NewForConfig(clientConfig) clientset, err := kubernetes.NewForConfig(clientConfig)
if err != nil { if err != nil {
err = errors.Wrap(err, "Unable to create clientset") err = fmt.Errorf("Unable to create clientset: %w", err)
return nil, nil, err return nil, nil, err
} }
return clientset, clientConfig, nil return clientset, clientConfig, nil
@@ -186,7 +181,7 @@ func (qp *QliksensePreflight) createPreflightTestDeployment(clientset *kubernete
result, err = deploymentsClient.Create(deployment) result, err = deploymentsClient.Create(deployment)
return err return err
}); err != nil { }); err != nil {
err = errors.Wrapf(err, "unable to create deployments in the %s namespace", namespace) err = fmt.Errorf("unable to create deployments in the %s namespace: %w", namespace, err)
return nil, err return nil, err
} }
qp.P.LogVerboseMessage("Created deployment %q\n", result.GetObjectMeta().GetName()) qp.P.LogVerboseMessage("Created deployment %q\n", result.GetObjectMeta().GetName())
@@ -201,7 +196,7 @@ func getDeployment(clientset *kubernetes.Clientset, namespace, depName string) (
deployment, err = deploymentsClient.Get(depName, v1.GetOptions{}) deployment, err = deploymentsClient.Get(depName, v1.GetOptions{})
return err return err
}); err != nil { }); err != nil {
err = errors.Wrapf(err, "unable to get deployments in the %s namespace", namespace) err = fmt.Errorf("unable to get deployments in the %s namespace: %w", namespace, err)
api.LogDebugMessage("%v\n", err) api.LogDebugMessage("%v\n", err)
return nil, err return nil, err
} }
@@ -271,7 +266,7 @@ func getService(clientset *kubernetes.Clientset, namespace, svcName string) (*ap
svc, err = servicesClient.Get(svcName, v1.GetOptions{}) svc, err = servicesClient.Get(svcName, v1.GetOptions{})
return err return err
}); err != nil { }); err != nil {
err = errors.Wrapf(err, "unable to get services in the %s namespace", namespace) err = fmt.Errorf("unable to get services in the %s namespace: %w", namespace, err)
return nil, err return nil, err
} }
@@ -288,7 +283,6 @@ func (qp *QliksensePreflight) deleteService(clientset *kubernetes.Clientset, nam
if err := retryOnError(func() (err error) { if err := retryOnError(func() (err error) {
return servicesClient.Delete(name, &deleteOptions) return servicesClient.Delete(name, &deleteOptions)
}); err != nil { }); err != nil {
fmt.Println(err)
return err return err
} }
qp.P.LogVerboseMessage("Deleted service: %s\n", name) qp.P.LogVerboseMessage("Deleted service: %s\n", name)
@@ -315,7 +309,7 @@ func (qp *QliksensePreflight) deletePod(clientset *kubernetes.Clientset, namespa
return nil return nil
} }
func (qp *QliksensePreflight) createPreflightTestPod(clientset *kubernetes.Clientset, namespace, podName, imageName string, secretNames []string, commandToRun []string) (*apiv1.Pod, error) { 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 // build the pod definition we want to deploy
pod := &apiv1.Pod{ pod := &apiv1.Pod{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: v1.ObjectMeta{
@@ -338,7 +332,7 @@ func (qp *QliksensePreflight) createPreflightTestPod(clientset *kubernetes.Clien
}, },
} }
if len(secretNames) > 0 { if len(secretNames) > 0 {
for _, secretName := range secretNames { for secretName, mountPath := range secretNames {
pod.Spec.Volumes = append(pod.Spec.Volumes, apiv1.Volume{ pod.Spec.Volumes = append(pod.Spec.Volumes, apiv1.Volume{
Name: secretName, Name: secretName,
VolumeSource: apiv1.VolumeSource{ VolumeSource: apiv1.VolumeSource{
@@ -347,7 +341,7 @@ func (qp *QliksensePreflight) createPreflightTestPod(clientset *kubernetes.Clien
Items: []apiv1.KeyToPath{ Items: []apiv1.KeyToPath{
{ {
Key: secretName, Key: secretName,
Path: secretName, Path: filepath.Base(mountPath),
}, },
}, },
}, },
@@ -356,7 +350,7 @@ func (qp *QliksensePreflight) createPreflightTestPod(clientset *kubernetes.Clien
if len(pod.Spec.Containers) > 0 { if len(pod.Spec.Containers) > 0 {
pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, apiv1.VolumeMount{ pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, apiv1.VolumeMount{
Name: secretName, Name: secretName,
MountPath: "/etc/ssl/" + secretName, MountPath: filepath.Dir(mountPath),
ReadOnly: true, ReadOnly: true,
}) })
} }
@@ -471,13 +465,13 @@ func waitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Po
} }
validateFunc := func(data interface{}) bool { validateFunc := func(data interface{}) bool {
po := data.(*apiv1.Pod) po := data.(*apiv1.Pod)
return len(po.Status.ContainerStatuses) > 0 && po.Status.ContainerStatuses[0].Ready return po.Status.Phase == apiv1.PodRunning || po.Status.Phase == apiv1.PodSucceeded || po.Status.Phase == apiv1.PodFailed
} }
if err := waitForResource(checkFunc, validateFunc); err != nil { if err := waitForResource(checkFunc, validateFunc); err != nil {
return err return err
} }
if len(pod.Status.ContainerStatuses) == 0 || !pod.Status.ContainerStatuses[0].Ready { 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") err = fmt.Errorf("container is taking much longer than expected")
return err return err
} }
@@ -492,7 +486,6 @@ func waitForPodToDie(clientset *kubernetes.Clientset, namespace string, pod *api
err = fmt.Errorf("unable to retrieve %s pod by name", podName) err = fmt.Errorf("unable to retrieve %s pod by name", podName)
return nil, err return nil, err
} }
api.LogDebugMessage("pod status: %v\n", po.Status.Phase)
return po, nil return po, nil
} }
validateFunc := func(r interface{}) bool { validateFunc := func(r interface{}) bool {
@@ -568,18 +561,20 @@ func (qp *QliksensePreflight) createPfRole(clientset *kubernetes.Clientset, name
return role, nil return role, nil
} }
func (qp *QliksensePreflight) deleteRole(clientset *kubernetes.Clientset, namespace string, role *v1beta1.Role) { func (qp *QliksensePreflight) deleteRole(clientset *kubernetes.Clientset, namespace string, roleName string) error {
rolesClient := clientset.RbacV1beta1().Roles(namespace) rolesClient := clientset.RbacV1beta1().Roles(namespace)
deletePolicy := v1.DeletePropagationForeground deletePolicy := v1.DeletePropagationForeground
deleteOptions := v1.DeleteOptions{ deleteOptions := v1.DeleteOptions{
PropagationPolicy: &deletePolicy, PropagationPolicy: &deletePolicy,
} }
err := rolesClient.Delete(role.GetName(), &deleteOptions) err := rolesClient.Delete(roleName, &deleteOptions)
if err != nil { if err != nil {
log.Fatal(err) log.Printf("Error: %v\n", err)
return err
} }
qp.P.LogVerboseMessage("Deleted role: %s\n\n", role.Name) qp.P.LogVerboseMessage("Deleted role: %s\n\n", roleName)
return nil
} }
func (qp *QliksensePreflight) createPfRoleBinding(clientset *kubernetes.Clientset, namespace, roleBindingName string) (*v1beta1.RoleBinding, error) { func (qp *QliksensePreflight) createPfRoleBinding(clientset *kubernetes.Clientset, namespace, roleBindingName string) (*v1beta1.RoleBinding, error) {
@@ -619,18 +614,20 @@ func (qp *QliksensePreflight) createPfRoleBinding(clientset *kubernetes.Clientse
return roleBinding, nil return roleBinding, nil
} }
func (qp *QliksensePreflight) deleteRoleBinding(clientset *kubernetes.Clientset, namespace string, roleBinding *v1beta1.RoleBinding) { func (qp *QliksensePreflight) deleteRoleBinding(clientset *kubernetes.Clientset, namespace string, roleBindingName string) error {
roleBindingClient := clientset.RbacV1beta1().RoleBindings(namespace) roleBindingClient := clientset.RbacV1beta1().RoleBindings(namespace)
deletePolicy := v1.DeletePropagationForeground deletePolicy := v1.DeletePropagationForeground
deleteOptions := v1.DeleteOptions{ deleteOptions := v1.DeleteOptions{
PropagationPolicy: &deletePolicy, PropagationPolicy: &deletePolicy,
} }
err := roleBindingClient.Delete(roleBinding.GetName(), &deleteOptions) err := roleBindingClient.Delete(roleBindingName, &deleteOptions)
if err != nil { if err != nil {
log.Fatal(err) log.Printf("Error: %v\n", err)
return err
} }
qp.P.LogVerboseMessage("Deleted RoleBinding: %s\n\n", roleBinding.Name) qp.P.LogVerboseMessage("Deleted RoleBinding: %s\n\n", roleBindingName)
return nil
} }
func (qp *QliksensePreflight) createPfServiceAccount(clientset *kubernetes.Clientset, namespace, serviceAccountName string) (*apiv1.ServiceAccount, error) { func (qp *QliksensePreflight) createPfServiceAccount(clientset *kubernetes.Clientset, namespace, serviceAccountName string) (*apiv1.ServiceAccount, error) {
@@ -657,18 +654,20 @@ func (qp *QliksensePreflight) createPfServiceAccount(clientset *kubernetes.Clien
return serviceAccount, nil return serviceAccount, nil
} }
func (qp *QliksensePreflight) deleteServiceAccount(clientset *kubernetes.Clientset, namespace string, serviceAccount *apiv1.ServiceAccount) { func (qp *QliksensePreflight) deleteServiceAccount(clientset *kubernetes.Clientset, namespace string, serviceAccountName string) error {
serviceAccountClient := clientset.CoreV1().ServiceAccounts(namespace) serviceAccountClient := clientset.CoreV1().ServiceAccounts(namespace)
deletePolicy := v1.DeletePropagationForeground deletePolicy := v1.DeletePropagationForeground
deleteOptions := v1.DeleteOptions{ deleteOptions := v1.DeleteOptions{
PropagationPolicy: &deletePolicy, PropagationPolicy: &deletePolicy,
} }
err := serviceAccountClient.Delete(serviceAccount.GetName(), &deleteOptions) err := serviceAccountClient.Delete(serviceAccountName, &deleteOptions)
if err != nil { if err != nil {
log.Fatal(err) log.Printf("Error: %v\n", err)
return err
} }
qp.P.LogVerboseMessage("Deleted ServiceAccount: %s\n\n", serviceAccount.Name) 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) { func (qp *QliksensePreflight) createPreflightTestSecret(clientset *kubernetes.Clientset, namespace, secretName string, secretData []byte) (*apiv1.Secret, error) {
@@ -699,16 +698,42 @@ func (qp *QliksensePreflight) createPreflightTestSecret(clientset *kubernetes.Cl
return secret, nil return secret, nil
} }
func (qp *QliksensePreflight) deleteK8sSecret(clientset *kubernetes.Clientset, namespace string, k8sSecret *apiv1.Secret) { func (qp *QliksensePreflight) deleteK8sSecret(clientset *kubernetes.Clientset, namespace string, secretName string) error {
secretClient := clientset.CoreV1().Secrets(namespace) secretClient := clientset.CoreV1().Secrets(namespace)
deletePolicy := v1.DeletePropagationForeground deletePolicy := v1.DeletePropagationForeground
deleteOptions := v1.DeleteOptions{ deleteOptions := v1.DeleteOptions{
PropagationPolicy: &deletePolicy, PropagationPolicy: &deletePolicy,
} }
err := secretClient.Delete(k8sSecret.GetName(), &deleteOptions) err := secretClient.Delete(secretName, &deleteOptions)
if err != nil { if err != nil {
log.Fatal(err) return err
} }
qp.P.LogVerboseMessage("Deleted Secret: %s\n", k8sSecret.Name) 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.P.LogVerboseMessage("Removing deployment...\n")
qp.CheckDeployment(namespace, kubeConfigContents, true)
qp.P.LogVerboseMessage("Removing service...\n")
qp.CheckService(namespace, kubeConfigContents, true)
qp.P.LogVerboseMessage("Removing pod...\n")
qp.CheckPod(namespace, kubeConfigContents, true)
qp.P.LogVerboseMessage("Removing role...\n")
qp.CheckCreateRole(namespace, true)
qp.P.LogVerboseMessage("Removing rolebinding...\n")
qp.CheckCreateRoleBinding(namespace, true)
qp.P.LogVerboseMessage("Removing serviceaccount...\n")
qp.CheckCreateServiceAccount(namespace, true)
qp.P.LogVerboseMessage("Removing DNS check components...\n")
qp.CheckDns(namespace, kubeConfigContents, true)
qp.P.LogVerboseMessage("Removing mongo check components...\n")
qp.CheckMongo(kubeConfigContents, namespace, &PreflightOptions{MongoOptions: &MongoOptions{}}, true)
return nil
} }

View File

@@ -11,48 +11,59 @@ import (
"github.com/qlik-oss/sense-installer/pkg/qliksense" "github.com/qlik-oss/sense-installer/pkg/qliksense"
) )
var resultYamlBytes = []byte("") func (qp *QliksensePreflight) CheckCreateRole(namespace string, cleanup bool) error {
func (qp *QliksensePreflight) CheckCreateRole(namespace string) error {
// create a Role // create a Role
qp.P.LogVerboseMessage("Preflight role check: \n") if !cleanup {
qp.P.LogVerboseMessage("--------------------- \n") qp.P.LogVerboseMessage("Preflight role check: \n")
err := qp.checkCreateEntity(namespace, "Role") qp.P.LogVerboseMessage("--------------------- \n")
}
err := qp.checkCreateEntity(namespace, "Role", cleanup)
if err != nil { if err != nil {
return err return err
} }
qp.P.LogVerboseMessage("Completed preflight role check\n") if !cleanup {
qp.P.LogVerboseMessage("Completed preflight role check\n")
}
return nil return nil
} }
func (qp *QliksensePreflight) CheckCreateRoleBinding(namespace string) error { func (qp *QliksensePreflight) CheckCreateRoleBinding(namespace string, cleanup bool) error {
// create a RoleBinding // create a RoleBinding
qp.P.LogVerboseMessage("Preflight rolebinding check: \n") if !cleanup {
qp.P.LogVerboseMessage("---------------------------- \n") qp.P.LogVerboseMessage("Preflight rolebinding check: \n")
err := qp.checkCreateEntity(namespace, "RoleBinding") qp.P.LogVerboseMessage("---------------------------- \n")
}
err := qp.checkCreateEntity(namespace, "RoleBinding", cleanup)
if err != nil { if err != nil {
return err return err
} }
qp.P.LogVerboseMessage("Completed preflight rolebinding check\n") if !cleanup {
qp.P.LogVerboseMessage("Completed preflight rolebinding check\n")
}
return nil return nil
} }
func (qp *QliksensePreflight) CheckCreateServiceAccount(namespace string) error { func (qp *QliksensePreflight) CheckCreateServiceAccount(namespace string, cleanup bool) error {
// create a service account // create a service account
qp.P.LogVerboseMessage("Preflight serviceaccount check: \n") if !cleanup {
qp.P.LogVerboseMessage("------------------------------- \n") qp.P.LogVerboseMessage("Preflight serviceaccount check: \n")
err := qp.checkCreateEntity(namespace, "ServiceAccount") qp.P.LogVerboseMessage("------------------------------- \n")
}
err := qp.checkCreateEntity(namespace, "ServiceAccount", cleanup)
if err != nil { if err != nil {
return err return err
} }
qp.P.LogVerboseMessage("Completed preflight serviceaccount check\n") if !cleanup {
qp.P.LogVerboseMessage("Completed preflight serviceaccount check\n")
}
return nil return nil
} }
func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string) error { func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string, cleanup bool) error {
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome) qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
var currentCR *qapi.QliksenseCR var currentCR *qapi.QliksenseCR
mfroot := "" mfroot := ""
kusDir := "" kusDir := ""
resultYamlBytes := []byte("")
var err error var err error
currentCR, err = qConfig.GetCurrentCR() currentCR, err = qConfig.GetCurrentCR()
if err != nil { if err != nil {
@@ -89,6 +100,12 @@ func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string)
} }
namespace = "" // namespace is handled when generating the manifests namespace = "" // namespace is handled when generating the manifests
// check if entity already exists in the cluster, if so - delete it
api.KubectlDeleteVerbose(sa, namespace, qp.P.Verbose)
if cleanup {
return nil
}
defer func() { defer func() {
qp.P.LogVerboseMessage("Cleaning up resources...\n") qp.P.LogVerboseMessage("Cleaning up resources...\n")
err := api.KubectlDeleteVerbose(sa, namespace, qp.P.Verbose) err := api.KubectlDeleteVerbose(sa, namespace, qp.P.Verbose)
@@ -113,7 +130,7 @@ func (qp *QliksensePreflight) CheckCreateRB(namespace string, kubeConfigContents
qp.P.LogVerboseMessage("Preflight createRole check: \n") qp.P.LogVerboseMessage("Preflight createRole check: \n")
qp.P.LogVerboseMessage("--------------------------- \n") qp.P.LogVerboseMessage("--------------------------- \n")
errStr := strings.Builder{} errStr := strings.Builder{}
err1 := qp.checkCreateEntity(namespace, "Role") err1 := qp.checkCreateEntity(namespace, "Role", false)
if err1 != nil { if err1 != nil {
errStr.WriteString(err1.Error()) errStr.WriteString(err1.Error())
errStr.WriteString("\n") errStr.WriteString("\n")
@@ -125,7 +142,7 @@ func (qp *QliksensePreflight) CheckCreateRB(namespace string, kubeConfigContents
// create a roleBinding // create a roleBinding
qp.P.LogVerboseMessage("Preflight rolebinding check: \n") qp.P.LogVerboseMessage("Preflight rolebinding check: \n")
qp.P.LogVerboseMessage("---------------------------- \n") qp.P.LogVerboseMessage("---------------------------- \n")
err2 := qp.checkCreateEntity(namespace, "RoleBinding") err2 := qp.checkCreateEntity(namespace, "RoleBinding", false)
if err2 != nil { if err2 != nil {
errStr.WriteString(err2.Error()) errStr.WriteString(err2.Error())
errStr.WriteString("\n") errStr.WriteString("\n")
@@ -137,7 +154,7 @@ func (qp *QliksensePreflight) CheckCreateRB(namespace string, kubeConfigContents
// create a service account // create a service account
qp.P.LogVerboseMessage("Preflight serviceaccount check: \n") qp.P.LogVerboseMessage("Preflight serviceaccount check: \n")
qp.P.LogVerboseMessage("------------------------------- \n") qp.P.LogVerboseMessage("------------------------------- \n")
err3 := qp.checkCreateEntity(namespace, "ServiceAccount") err3 := qp.checkCreateEntity(namespace, "ServiceAccount", false)
if err3 != nil { if err3 != nil {
errStr.WriteString(err3.Error()) errStr.WriteString(err3.Error())
errStr.WriteString("\n") errStr.WriteString("\n")

View File

@@ -18,12 +18,12 @@ import (
b64 "encoding/base64" b64 "encoding/base64"
. "github.com/logrusorgru/aurora"
ansi "github.com/mattn/go-colorable" ansi "github.com/mattn/go-colorable"
"github.com/qlik-oss/sense-installer/pkg/api" "github.com/qlik-oss/sense-installer/pkg/api"
_ "gopkg.in/yaml.v2" _ "gopkg.in/yaml.v2"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
. "github.com/logrusorgru/aurora"
) )
const ( const (
@@ -62,7 +62,7 @@ func (q *Qliksense) SetSecrets(args []string, isSecretSet bool, base64Encoded bo
} }
// Metadata name in qliksense CR is the name of the current context // Metadata name in qliksense CR is the name of the current context
api.LogDebugMessage("Current context: %s", qliksenseCR.GetName()) api.LogDebugMessage("Current context: %s\n", qliksenseCR.GetName())
encryptionKey, err := qConfig.GetEncryptionKeyForCurrent() encryptionKey, err := qConfig.GetEncryptionKeyForCurrent()
if err != nil { if err != nil {
return err return err
@@ -72,7 +72,7 @@ func (q *Qliksense) SetSecrets(args []string, isSecretSet bool, base64Encoded bo
return err return err
} }
for _, ra := range resultArgs { for _, ra := range resultArgs {
api.LogDebugMessage("value args to be encrypted: %s", ra.Value) api.LogDebugMessage("value args to be encrypted: %s\n", ra.Value)
if err := q.processSecret(ra, encryptionKey, qliksenseCR, isSecretSet); err != nil { if err := q.processSecret(ra, encryptionKey, qliksenseCR, isSecretSet); err != nil {
return err return err
} }
@@ -213,8 +213,8 @@ func validateCR(key string, keySub string, value string, crSpec *api.QliksenseCR
} }
} else { } else {
switch key { switch key {
case "gitops": case "opsrunner":
crSpec.Spec.GitOps = &config.GitOps{} crSpec.Spec.OpsRunner = &config.OpsRunner{}
case "git": case "git":
crSpec.Spec.Git = &config.Repo{} crSpec.Spec.Git = &config.Repo{}
} }
@@ -248,8 +248,8 @@ func (q *Qliksense) SetOtherConfigs(args []string) error {
if err := q.processSetGit(arg, qliksenseCR); err != nil { if err := q.processSetGit(arg, qliksenseCR); err != nil {
return err return err
} }
} else if strings.HasPrefix(arg, "gitOps.") { } else if strings.HasPrefix(arg, "opsRunner.") {
if err := q.processSetGitOps(arg, qliksenseCR); err != nil { if err := q.processSetOpsRunner(arg, qliksenseCR); err != nil {
return err return err
} }
} else { } else {
@@ -337,27 +337,29 @@ func (q *Qliksense) processSetGit(arg string, cr *api.QliksenseCR) error {
return nil return nil
} }
func (q *Qliksense) processSetGitOps(arg string, cr *api.QliksenseCR) error { func (q *Qliksense) processSetOpsRunner(arg string, cr *api.QliksenseCR) error {
args := strings.Split(arg, "=") args := strings.Split(arg, "=")
subs := strings.Split(args[0], ".") subs := strings.Split(args[0], ".")
if cr.Spec.Git == nil { if cr.Spec.OpsRunner == nil {
cr.Spec.GitOps = &config.GitOps{} cr.Spec.OpsRunner = &config.OpsRunner{}
} }
switch subs[1] { switch subs[1] {
case "enabled": case "enabled":
if args[1] != "yes" && args[1] != "no" { if args[1] != "yes" && args[1] != "no" {
return errors.New("Please use yes or no for key enabled") return errors.New("Please use yes or no for key enabled")
} }
cr.Spec.GitOps.Enabled = args[1] cr.Spec.OpsRunner.Enabled = args[1]
case "schedule": case "schedule":
if _, err := cron.ParseStandard(args[1]); err != nil { if _, err := cron.ParseStandard(args[1]); err != nil {
return errors.New("Please enter string with standard cron scheduling syntax ") return errors.New("Please enter string with standard cron scheduling syntax ")
} }
cr.Spec.GitOps.Schedule = args[1] cr.Spec.OpsRunner.Schedule = args[1]
case "watchBranch": case "watchBranch":
cr.Spec.GitOps.WatchBranch = args[1] cr.Spec.OpsRunner.WatchBranch = args[1]
case "image": case "image":
cr.Spec.GitOps.Image = args[1] cr.Spec.OpsRunner.Image = args[1]
case "crPvc":
cr.Spec.OpsRunner.CrPvc = args[1]
default: default:
return errors.New(arg + " does not match any cr spec") return errors.New(arg + " does not match any cr spec")
} }
@@ -411,7 +413,7 @@ func (q *Qliksense) DeleteContextConfig(args []string, flag bool) error {
out := ansi.NewColorableStdout() out := ansi.NewColorableStdout()
switch args[0] { switch args[0] {
case qliksenseConfig.Spec.CurrentContext: case qliksenseConfig.Spec.CurrentContext:
fmt.Fprintln(out,Yellow("Please switch contexts to be able to delete this context.")) fmt.Fprintln(out, Yellow("Please switch contexts to be able to delete this context."))
err := fmt.Errorf(Red("Cannot delete current context - %s").String(), White(Bold(qliksenseConfig.Spec.CurrentContext))) err := fmt.Errorf(Red("Cannot delete current context - %s").String(), White(Bold(qliksenseConfig.Spec.CurrentContext)))
return err return err
case DefaultQliksenseContext: case DefaultQliksenseContext:
@@ -452,7 +454,7 @@ func (q *Qliksense) DeleteContextConfig(args []string, flag bool) error {
if ans == true { if ans == true {
api.WriteToFile(&qliksenseConfig, qliksenseConfigFile) api.WriteToFile(&qliksenseConfig, qliksenseConfigFile)
fmt.Fprintln(out, Yellow(Underline("Warning: Active resources may still be running in-cluster"))) fmt.Fprintln(out, Yellow(Underline("Warning: Active resources may still be running in-cluster")))
fmt.Fprintln(out, Green("Successfully deleted context: "),Bold(args[0])) fmt.Fprintln(out, Green("Successfully deleted context: "), Bold(args[0]))
} else { } else {
return nil return nil
} }
@@ -520,7 +522,7 @@ func (q *Qliksense) SetUpQliksenseContext(contextName string) error {
} }
// set the encrypted default mongo // set the encrypted default mongo
return q.SetSecrets([]string{`qliksense.mongoDbUri="mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"`}, false, false) return q.SetSecrets([]string{`qliksense.mongodbUri="mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"`}, false, false)
} }
func validateInput(input string) (string, error) { func validateInput(input string) (string, error) {

View File

@@ -29,7 +29,7 @@ const (
var targetFileStringTemplate = ` var targetFileStringTemplate = `
apiVersion: v1 apiVersion: v1
data: data:
mongoDbUri: %s mongodbUri: %s
kind: Secret kind: Secret
metadata: metadata:
name: testctx-qliksense-senseinstaller name: testctx-qliksense-senseinstaller
@@ -244,7 +244,7 @@ func TestSetOtherConfigs(t *testing.T) {
q: &Qliksense{ q: &Qliksense{
QliksenseHome: testDir, QliksenseHome: testDir,
}, },
args: []string{"profile=minikube", "rotateKeys=yes", "storageClassName=efs", "gitOps.enabled=yes", "gitOps.schedule=30 * * * *", "git.repository=master", "git.userName=foo", "git.accessToken=1234"}, args: []string{"profile=minikube", "rotateKeys=yes", "storageClassName=efs", "opsRunner.enabled=yes", "opsRunner.schedule=30 * * * *", "git.repository=master", "git.userName=foo", "git.accessToken=1234"},
}, },
wantErr: false, wantErr: false,
}, },
@@ -254,7 +254,7 @@ func TestSetOtherConfigs(t *testing.T) {
q: &Qliksense{ q: &Qliksense{
QliksenseHome: testDir, QliksenseHome: testDir,
}, },
args: []string{"someconfig=somevalue, gitOps.schedule=bar", "gitOps.enabled=bar", "git.foo=bar", "rotateKeys=bar"}, args: []string{"someconfig=somevalue, opsRunner.schedule=bar", "opsRunner.enabled=bar", "git.foo=bar", "rotateKeys=bar"},
}, },
wantErr: true, wantErr: true,
}, },
@@ -296,7 +296,7 @@ func TestSetConfigs(t *testing.T) {
q: &Qliksense{ q: &Qliksense{
QliksenseHome: testDir, QliksenseHome: testDir,
}, },
args: []string{"qliksense.acceptEULA=\"yes\"", "qliksense.mongoDbUri=\"mongo://mongo:3307\""}, args: []string{"qliksense.acceptEULA=\"yes\"", "qliksense.mongodbUri=\"mongo://mongo:3307\""},
}, },
wantErr: false, wantErr: false,
}, },
@@ -572,7 +572,7 @@ func Test_SetSecrets(t *testing.T) {
QliksenseHome: testDir, QliksenseHome: testDir,
}, },
args: args{ args: args{
args: []string{"qliksense.mongoDbUri=\"mongodb://qlik-default-mongodb:27017/qliksense?ssl=false\""}, args: []string{"qliksense.mongodbUri=\"mongodb://qlik-default-mongodb:27017/qliksense?ssl=false\""},
isSecretSet: false, isSecretSet: false,
}, },
wantErr: false, wantErr: false,
@@ -583,7 +583,7 @@ func Test_SetSecrets(t *testing.T) {
QliksenseHome: testDir, QliksenseHome: testDir,
}, },
args: args{ args: args{
args: []string{"qliksense.mongoDbUri=bW9uZ29kYjovL3FsaWstZGVmYXVsdC1tb25nb2RiOjI3MDE3L3FsaWtzZW5zZT9zc2w9ZmFsc2U="}, args: []string{"qliksense.mongodbUri=bW9uZ29kYjovL3FsaWstZGVmYXVsdC1tb25nb2RiOjI3MDE3L3FsaWtzZW5zZT9zc2w9ZmFsc2U="},
isSecretSet: false, isSecretSet: false,
base64: true, base64: true,
}, },
@@ -595,7 +595,7 @@ func Test_SetSecrets(t *testing.T) {
QliksenseHome: testDir, QliksenseHome: testDir,
}, },
args: args{ args: args{
args: []string{"qliksense.mongoDbUri=\"mongo://mongo:3307\""}, args: []string{"qliksense.mongodbUri=\"mongo://mongo:3307\""},
isSecretSet: true, isSecretSet: true,
}, },
wantErr: false, wantErr: false,
@@ -606,7 +606,7 @@ func Test_SetSecrets(t *testing.T) {
QliksenseHome: testDir, QliksenseHome: testDir,
}, },
args: args{ args: args{
args: []string{"qliksense.mongoDbUri=\"mongodb://qlik-default-mongodb:27017/qliksense?ssl=false\""}, args: []string{"qliksense.mongodbUri=\"mongodb://qlik-default-mongodb:27017/qliksense?ssl=false\""},
isSecretSet: true, isSecretSet: true,
}, },
wantErr: false, wantErr: false,

View File

@@ -0,0 +1,109 @@
package qliksense
import (
"fmt"
"strings"
"reflect"
kconfig "github.com/qlik-oss/k-apis/pkg/config"
"github.com/qlik-oss/sense-installer/pkg/api"
)
func (q *Qliksense) UnsetCmd(args []string) error {
return unsetAll(q.QliksenseHome, args)
}
func unsetAll(qHome string, args []string) error {
qConfig := api.NewQConfig(qHome)
qcr, err := qConfig.GetCurrentCR()
if err != nil {
return err
}
// either delete all args or none
for _, arg := range args {
isRemoved := false
// delete if key present
if !strings.Contains(arg, ".") {
if isRemoved = unsetOnlyKey(arg, qcr); isRemoved {
//continue to the next arg
continue
} else if isRemoved = unsetServiceName(arg, qcr); isRemoved {
//continue to the next arg
continue
} else {
return fmt.Errorf("%s not found in the context", arg)
}
}
// delete key inside configs if present
// delete key inside secrets if present
if isRemoved = unsetServiceKey(arg, qcr); !isRemoved {
return fmt.Errorf("%s not found in the context", arg)
}
}
return qConfig.WriteCR(qcr)
}
func unsetOnlyKey(key string, qcr *api.QliksenseCR) bool {
v := reflect.ValueOf(qcr.Spec).Elem().FieldByName(strings.Title(key))
if v.IsValid() && v.CanSet() {
v.SetString("")
return true
}
return false
}
func unsetServiceName(svc string, qcr *api.QliksenseCR) bool {
if qcr.Spec.Configs != nil && qcr.Spec.Configs[svc] != nil {
delete(qcr.Spec.Configs, svc)
return true
}
if qcr.Spec.Secrets != nil && qcr.Spec.Secrets[svc] != nil {
delete(qcr.Spec.Secrets, svc)
return true
}
return false
}
func unsetServiceKey(svcKey string, qcr *api.QliksenseCR) bool {
sk := strings.Split(svcKey, ".")
svc := sk[0]
key := sk[1]
if qcr.Spec.Configs != nil && qcr.Spec.Configs[svc] != nil {
index := findIndex(key, qcr.Spec.Configs[svc])
if index > -1 {
qcr.Spec.Configs[svc][index] = qcr.Spec.Configs[svc][len(qcr.Spec.Configs[svc])-1]
qcr.Spec.Configs[svc] = qcr.Spec.Configs[svc][:len(qcr.Spec.Configs[svc])-1]
if len(qcr.Spec.Configs[svc]) == 0 {
delete(qcr.Spec.Configs, svc)
}
return true
}
}
if qcr.Spec.Secrets != nil && qcr.Spec.Secrets[svc] != nil {
index := findIndex(key, qcr.Spec.Secrets[svc])
if index > -1 {
qcr.Spec.Secrets[svc][index] = qcr.Spec.Secrets[svc][len(qcr.Spec.Secrets[svc])-1]
qcr.Spec.Secrets[svc] = qcr.Spec.Secrets[svc][:len(qcr.Spec.Secrets[svc])-1]
if len(qcr.Spec.Secrets[svc]) == 0 {
delete(qcr.Spec.Secrets, svc)
}
return true
}
}
return false
}
func findIndex(elem string, nvs kconfig.NameValues) int {
for i, nv := range nvs {
if nv.Name == elem {
return i
}
}
return -1
}

View File

@@ -0,0 +1,99 @@
package qliksense
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/qlik-oss/sense-installer/pkg/api"
_ "gopkg.in/yaml.v2"
)
func TestUnsetAll(t *testing.T) {
qHome, _ := ioutil.TempDir("", "")
testPepareDir(qHome)
defer os.RemoveAll(qHome)
//fmt.Print(qHome)
args := []string{"rotateKeys", "qliksense", "qliksense2.acceptEula3", "serviceA.acceptEula"}
if err := unsetAll(qHome, args); err != nil {
t.Log("error during unset", err)
t.FailNow()
}
qc := api.NewQConfig(qHome)
qcr, err := qc.GetCurrentCR()
if err != nil {
t.Log("error while getting current cr", err)
t.FailNow()
}
if qcr.Spec.RotateKeys != "" {
t.Log("Expected empty rotateKeys but got: " + qcr.Spec.RotateKeys)
t.Fail()
}
if qcr.Spec.Configs["qliksense"] != nil {
t.Log("qliksense in configs not deleted")
t.Fail()
}
if len(qcr.Spec.Configs["qliksense2"]) != 1 {
t.Log("qliksense2.acceptEula3 not deleted")
t.Fail()
}
if qcr.Spec.Configs["serviceA"] != nil {
t.Log("serviceA not deleted")
t.Fail()
}
}
func testPepareDir(qHome string) {
config :=
`
apiVersion: config.qlik.com/v1
kind: QliksenseConfig
metadata:
name: qliksenseConfig
spec:
contexts:
- name: qlik-default
crFile: contexts/qlik-default/qlik-default.yaml
currentContext: qlik-default
`
configFile := filepath.Join(qHome, "config.yaml")
// tests/config.yaml exists
ioutil.WriteFile(configFile, []byte(config), 0777)
contextYaml :=
`
apiVersion: qlik.com/v1
kind: Qliksense
metadata:
name: qlik-default
spec:
profile: docker-desktop
rotateKeys: "yes"
configs:
qliksense:
- name: acceptEula
value: some
qliksense2:
- name: acceptEula2
value: some
- name: acceptEula3
value: some
serviceA:
- name: acceptEula
value: some
`
qlikDefaultContext := "qlik-default"
// create contexts/qlik-default/ under tests/
contexts := "contexts"
contextsDir := filepath.Join(qHome, contexts, qlikDefaultContext)
if err := os.MkdirAll(contextsDir, 0777); err != nil {
err = fmt.Errorf("Not able to create directories")
}
contextFile := filepath.Join(contextsDir, qlikDefaultContext+".yaml")
ioutil.WriteFile(contextFile, []byte(contextYaml), 0777)
}

View File

@@ -95,9 +95,9 @@ func (q *Qliksense) PullImagesForCurrentCR() error {
return nil return nil
} }
func (q *Qliksense) appendGitOpsImage(images *[]string, qcr *qapi.QliksenseCR) { func (q *Qliksense) appendOpsRunnerImage(images *[]string, qcr *qapi.QliksenseCR) {
if qcr.Spec.GitOps != nil && qcr.Spec.GitOps.Image != "" { if qcr.Spec.OpsRunner != nil && qcr.Spec.OpsRunner.Image != "" {
*images = append(*images, qcr.Spec.GitOps.Image) *images = append(*images, qcr.Spec.OpsRunner.Image)
} }
} }
@@ -212,7 +212,7 @@ func (q *Qliksense) appendAdditionalImages(images *[]string, qcr *qapi.Qliksense
if err := q.appendOperatorImages(images); err != nil { if err := q.appendOperatorImages(images); err != nil {
return err return err
} }
q.appendGitOpsImage(images, qcr) q.appendOpsRunnerImage(images, qcr)
q.appendPreflightImages(images) q.appendPreflightImages(images)
return nil return nil
} }

View File

@@ -186,7 +186,7 @@ kind: Qliksense
metadata: metadata:
name: qlik-default name: qlik-default
spec: spec:
gitOps: opsRunner:
image: some-gitops-image image: some-gitops-image
`) `)
@@ -225,7 +225,7 @@ spec:
return false return false
} }
if !haveMatchingImage(func(image string) bool { if !haveMatchingImage(func(image string) bool {
return strings.Contains(image, "qlik-docker-oss.bintray.io/qliksense-operator:v") return strings.Contains(image, "qlik-docker-oss.bintray.io/qliksense-operator:")
}) { }) {
t.Fatal("expected to find the operator image in the list, but it wasn't there") t.Fatal("expected to find the operator image in the list, but it wasn't there")
} }
@@ -245,7 +245,7 @@ spec:
t.Fatal("expected to find the netcat Preflight image in the list, but it wasn't there") t.Fatal("expected to find the netcat Preflight image in the list, but it wasn't there")
} }
if !haveMatchingImage(func(image string) bool { if !haveMatchingImage(func(image string) bool {
return image == "mongo" 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 mongo Preflight image in the list, but it wasn't there")
} }

View File

@@ -8,7 +8,9 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"github.com/mitchellh/go-homedir"
"github.com/qlik-oss/k-apis/pkg/config" "github.com/qlik-oss/k-apis/pkg/config"
"github.com/qlik-oss/k-apis/pkg/cr"
"sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/filesys"
qapi "github.com/qlik-oss/sense-installer/pkg/api" qapi "github.com/qlik-oss/sense-installer/pkg/api"
@@ -16,8 +18,9 @@ import (
type InstallCommandOptions struct { type InstallCommandOptions struct {
StorageClass string StorageClass string
MongoDbUri string MongodbUri string
RotateKeys string RotateKeys string
DryRun bool
} }
func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, keepPatchFiles bool) error { func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, keepPatchFiles bool) error {
@@ -42,8 +45,8 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
} }
qcr.SetEULA("yes") qcr.SetEULA("yes")
if opts.MongoDbUri != "" { if opts.MongodbUri != "" {
qcr.Spec.AddToSecrets("qliksense", "mongoDbUri", opts.MongoDbUri, "") qcr.Spec.AddToSecrets("qliksense", "mongodbUri", opts.MongodbUri, "")
} }
if opts.StorageClass != "" { if opts.StorageClass != "" {
qcr.Spec.StorageClassName = opts.StorageClass qcr.Spec.StorageClassName = opts.StorageClass
@@ -51,11 +54,18 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
if opts.RotateKeys != "" { if opts.RotateKeys != "" {
qcr.Spec.RotateKeys = opts.RotateKeys qcr.Spec.RotateKeys = opts.RotateKeys
} }
// for debugging purpose
if opts.DryRun {
// generate patches
qcr.Spec.RotateKeys = "None"
userHomeDir, _ := homedir.Dir()
fmt.Println("Generating patches only")
cr.GeneratePatches(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config"))
return nil
}
qConfig.WriteCurrentContextCR(qcr) qConfig.WriteCurrentContextCR(qcr)
//if the docker pull secret exists on disk, install it in the cluster if err := applyImagePullSecret(qConfig); err != nil {
//if it doesn't exist on disk, remove it in the cluster
if err := installOrRemoveImagePullSecret(qConfig); err != nil {
return err return err
} }
@@ -132,22 +142,13 @@ func (q *Qliksense) getProcessedOperatorControllerString(qcr *qapi.QliksenseCR)
return operatorControllerString, nil return operatorControllerString, nil
} }
func installOrRemoveImagePullSecret(qConfig *qapi.QliksenseConfig) error { func applyImagePullSecret(qConfig *qapi.QliksenseConfig) error {
if pullDockerConfigJsonSecret, err := qConfig.GetPullDockerConfigJsonSecret(); err == nil { if pullDockerConfigJsonSecret, err := qConfig.GetPullDockerConfigJsonSecret(); err == nil {
if dockerConfigJsonSecretYaml, err := pullDockerConfigJsonSecret.ToYaml(""); err != nil { if dockerConfigJsonSecretYaml, err := pullDockerConfigJsonSecret.ToYaml(""); err != nil {
return err return err
} else if err := qapi.KubectlApply(string(dockerConfigJsonSecretYaml), ""); err != nil { } else if err := qapi.KubectlApply(string(dockerConfigJsonSecretYaml), ""); err != nil {
return err return err
} }
} else {
deleteDockerConfigJsonSecret := qapi.DockerConfigJsonSecret{
Name: pullSecretName,
}
if deleteDockerConfigJsonSecretYaml, err := deleteDockerConfigJsonSecret.ToYaml(""); err != nil {
return err
} else if err := qapi.KubectlDelete(string(deleteDockerConfigJsonSecretYaml), ""); err != nil {
qapi.LogDebugMessage("failed deleting %v, error: %v\n", pullSecretName, err)
}
} }
return nil return nil
} }

View File

@@ -43,7 +43,7 @@ spec:
value: "yes" value: "yes"
secrets: secrets:
qliksense: qliksense:
- name: mongoDbUri - name: mongodbUri
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
profile: docker-desktop profile: docker-desktop
rotateKeys: "yes"` rotateKeys: "yes"`

View File

@@ -22,7 +22,7 @@ import (
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v2"
"github.com/Shopify/ejson" "github.com/Shopify/ejson"
"github.com/qlik-oss/k-apis/pkg/config" "github.com/qlik-oss/k-apis/pkg/config"
@@ -310,8 +310,8 @@ func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
} }
break break
} }
if resource["kind"].(string) == "Secret" && strings.Contains(resource["metadata"].(map[string]interface{})["name"].(string), "users-secrets-") { if resource["kind"].(string) == "Secret" && strings.Contains(resource["metadata"].(map[interface {}]interface {})["name"].(string), "users-secrets-") {
keyIdBase64 = resource["data"].(map[string]interface{})["tokenAuthPrivateKeyId"].(string) keyIdBase64 = resource["data"].(map[interface {}]interface {})["tokenAuthPrivateKeyId"].(string)
break break
} }
} }

View File

@@ -35,7 +35,7 @@ spec:
value: "yes" value: "yes"
secrets: secrets:
qliksense: qliksense:
- name: mongoDbUri - name: mongodbUri
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
profile: docker-desktop profile: docker-desktop
rotateKeys: "yes"` rotateKeys: "yes"`
@@ -62,7 +62,7 @@ spec:
value: "yes" value: "yes"
secrets: secrets:
qliksense: qliksense:
- name: mongoDbUri - name: mongodbUri
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
profile: docker-desktop profile: docker-desktop
rotateKeys: "yes"` rotateKeys: "yes"`