Compare commits

...

11 Commits

Author SHA1 Message Date
Ashwathi Shiva
33ab6c632b Merge branch 'master' into autorun_postflight_checks 2020-06-18 09:06:00 -04:00
Andriy Bulynko
6899a7be77 go-git and test fixes (#431) 2020-06-18 08:40:38 -04:00
Ashwathi Shiva
d3f33507d0 updated docs, added a postflight all command at this point 2020-06-17 00:52:31 -04:00
Ashwathi Shiva
a619970385 postflight checks triggerred on qliksense install 2020-06-17 00:09:26 -04:00
Foysal Iqbal
293e923c82 Merge pull request #409 from qlik-oss/fix-unset
fix unset
2020-06-12 23:30:07 -04:00
Foysal Iqbal
504190c75c fix unset
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-12 17:36:04 -04:00
Andriy Bulynko
fc62e5fea4 Upgrading kustomize API to version qlik/v0.0.28 (#408) 2020-06-11 23:18:31 -04:00
Andriy Bulynko
bf4e4b9bf1 Require EULA acceptance for install only (#405) 2020-06-11 10:35:41 -04:00
Ashwathi Shiva
4cb3231a08 Preflight images pinned to a specific tag (#404)
* Pinned netcat, nginx and preflight-mongo images to specific tags
2020-06-11 09:32:53 -04:00
Ashwathi Shiva
e1dbcfaac8 added a better error message to a specific scenario in preflight role/rolebinding/serviceaccount check (#403) 2020-06-09 23:32:05 -04:00
Foysal Iqbal
77a3bf4581 Merge pull request #401 from qlik-oss/upgrade-kapi
upgrade k-api and remove config-apply
2020-06-09 22:45:52 -04:00
25 changed files with 339 additions and 508 deletions

View File

@@ -1,74 +1,45 @@
package main
import (
"bytes"
"errors"
"fmt"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
)
func applyCmd(q *qliksense.Qliksense) *cobra.Command {
opts := &qliksense.InstallCommandOptions{}
opts := &qliksense.InstallCommandOptions{
CleanPatchFiles: true,
}
filePath := ""
cleanPatchFiles, pull, push := true, false, false
c := &cobra.Command{
Use: "apply",
Short: "install qliksense based on provided cr file",
Long: `install qliksense based on provided cr file`,
Example: `qliksense apply -f file_name or cat cr_file | qliksense apply -f -`,
RunE: func(cmd *cobra.Command, args []string) error {
return runLoadOrApplyCommandE(cmd, func(crBytes []byte) error {
if cr, crBytesWithEula, err := getCrWithEulaInserted(crBytes); err != nil {
return err
} else if err := validatePullPushFlagsOnApply(cr, pull, push); err != nil {
return err
} else {
return q.ApplyCRFromReader(bytes.NewReader(crBytesWithEula), opts, cleanPatchFiles, true, pull, push)
}
})
return apply(q, cmd, opts)
},
}
f := c.Flags()
f.StringVarP(&filePath, "file", "f", "", "Install from a CR file")
c.MarkFlagRequired("file")
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.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(&cleanPatchFiles, cleanPatchFilesFlagName, cleanPatchFiles, cleanPatchFilesFlagUsage)
f.BoolVarP(&pull, pullFlagName, pullFlagShorthand, pull, pullFlagUsage)
f.BoolVarP(&push, pushFlagName, pushFlagShorthand, push, pushFlagUsage)
eulaPreRunHooks.addValidator(fmt.Sprintf("%v %v", rootCommandName, c.Name()), loadOrApplyCommandEulaPreRunHook)
f.BoolVar(&opts.CleanPatchFiles, cleanPatchFilesFlagName, opts.CleanPatchFiles, cleanPatchFilesFlagUsage)
f.BoolVarP(&opts.Pull, pullFlagName, pullFlagShorthand, opts.Pull, pullFlagUsage)
f.BoolVarP(&opts.Push, pushFlagName, pushFlagShorthand, opts.Push, pushFlagUsage)
f.StringVarP(&opts.AcceptEULA, "acceptEULA", "a", opts.AcceptEULA, "Accept EULA for qliksense")
if err := c.MarkFlagRequired("file"); err != nil {
panic(err)
}
return c
}
func validatePullPushFlagsOnApply(cr *qapi.QliksenseCR, pull, push bool) error {
if pull && !push {
fmt.Printf("WARNING: pulling images without pushing them")
}
if push {
if registry := cr.Spec.GetImageRegistry(); registry == "" {
return errors.New("no image registry set in the CR; to set it use: qliksense config set-image-registry")
}
}
return nil
}
func getCrWithEulaInserted(crBytes []byte) (*qapi.QliksenseCR, []byte, error) {
if cr, err := qapi.CreateCRObjectFromString(string(crBytes)); err != nil {
return nil, nil, err
func apply(q *qliksense.Qliksense, cmd *cobra.Command, opts *qliksense.InstallCommandOptions) error {
if crBytes, err := getCrBytesFromFileFlag(cmd); err != nil {
return err
} else {
cr.SetEULA("yes")
if crBytesWithEula, err := qapi.K8sToYaml(cr); err != nil {
return nil, nil, err
} else {
return cr, crBytesWithEula, nil
}
return q.ApplyCRFromBytes(crBytes, opts, true)
}
}

View File

@@ -1,109 +0,0 @@
package main
import (
"fmt"
"os"
"strings"
"github.com/mattn/go-tty"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
)
type eulaPreRunHooksT struct {
validators map[string]func(cmd *cobra.Command, q *qliksense.Qliksense) (bool, error)
postValidationArtifacts map[string]interface{}
}
func (e *eulaPreRunHooksT) addValidator(command string, validator func(cmd *cobra.Command, q *qliksense.Qliksense) (bool, error)) {
e.validators[command] = validator
}
func (e *eulaPreRunHooksT) getValidator(command string) func(cmd *cobra.Command, q *qliksense.Qliksense) (bool, error) {
if validator, ok := e.validators[command]; ok {
return validator
}
return nil
}
func (e *eulaPreRunHooksT) addPostValidationArtifact(artifactName string, artifact interface{}) {
e.postValidationArtifacts[artifactName] = artifact
}
func (e *eulaPreRunHooksT) getPostValidationArtifact(artifactName string) interface{} {
if artifact, ok := e.postValidationArtifacts[artifactName]; ok {
return artifact
}
return nil
}
var eulaEnforced = os.Getenv("QLIKSENSE_EULA_ENFORCE") == "true"
var eulaText = "Please read the end user license agreement at: https://www.qlik.com/us/legal/license-terms"
var eulaPrompt = "Do you accept our EULA? (y/n): "
var eulaErrorInstruction = `You must enter "y" to continue or execute the command with the acceptEULA flag set to "yes"`
var eulaPreRunHooks = eulaPreRunHooksT{
validators: make(map[string]func(cmd *cobra.Command, q *qliksense.Qliksense) (bool, error)),
postValidationArtifacts: make(map[string]interface{}),
}
func commandAlwaysRequiresEulaAcceptance(commandName string) bool {
return commandName == fmt.Sprintf("%v install", rootCommandName) ||
commandName == fmt.Sprintf("%v apply", rootCommandName)
}
func globalEulaPreRun(cmd *cobra.Command, q *qliksense.Qliksense) {
if isEulaEnforced(cmd.CommandPath()) {
eulaFlagValue := strings.TrimSpace(strings.ToLower(cmd.Flag("acceptEULA").Value.String()))
if eulaFlagValue != "" && eulaFlagValue != "yes" {
doEnforceEula()
} else if eulaFlagValue == "" {
if eulaPreRunHook := eulaPreRunHooks.getValidator(cmd.CommandPath()); eulaPreRunHook != nil {
if eulaAccepted, err := eulaPreRunHook(cmd, q); err != nil {
panic(err)
} else if !eulaAccepted {
doEnforceEula()
}
} else if qConfig, err := qapi.NewQConfigE(q.QliksenseHome); err != nil {
doEnforceEula()
} else if qcr, err := qConfig.GetCurrentCR(); err != nil || !qcr.IsEULA() {
doEnforceEula()
}
}
}
}
func globalEulaPostRun(cmd *cobra.Command, q *qliksense.Qliksense) {
if isEulaEnforced(cmd.CommandPath()) {
if err := q.SetEulaAccepted(); err != nil {
panic(err)
}
}
}
func isEulaEnforced(commandName string) bool {
return eulaEnforced || commandAlwaysRequiresEulaAcceptance(commandName)
}
func doEnforceEula() {
fmt.Println(eulaText)
fmt.Print(eulaPrompt)
answer := readRuneFromTty()
if strings.ToLower(answer) != "y" {
fmt.Println(eulaErrorInstruction)
os.Exit(1)
}
}
func readRuneFromTty() string {
t, err := tty.Open()
if err != nil {
panic(err)
}
defer t.Close()
answer, err := t.ReadString()
if err != nil {
panic(err)
}
return answer
}

View File

@@ -1,19 +1,15 @@
package main
import (
"bytes"
"fmt"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
)
func installCmd(q *qliksense.Qliksense) *cobra.Command {
opts := &qliksense.InstallCommandOptions{}
opts := &qliksense.InstallCommandOptions{
CleanPatchFiles: true,
}
filePath := ""
cleanPatchFiles, pull, push := true, false, false
c := &cobra.Command{
Use: "install",
Short: "install a qliksense release",
@@ -22,76 +18,34 @@ func installCmd(q *qliksense.Qliksense) *cobra.Command {
# qliksense install -f file_name or cat cr_file | qliksense install -f -
`,
RunE: func(cmd *cobra.Command, args []string) error {
version := ""
if len(args) != 0 {
version = args[0]
}
if filePath != "" {
return runLoadOrApplyCommandE(cmd, func(crBytes []byte) error {
if cr, crBytesWithEula, err := getCrWithEulaInserted(crBytes); err != nil {
return err
} else if err := validatePullPushFlagsOnApply(cr, pull, push); err != nil {
return err
} else {
return q.ApplyCRFromReader(bytes.NewReader(crBytesWithEula), opts, cleanPatchFiles, true, pull, push)
}
})
} else {
version := ""
if len(args) != 0 {
version = args[0]
}
if err := validatePullPushFlagsOnInstall(q, pull, push); err != nil {
if err := apply(q, cmd, opts); err != nil {
return err
}
if pull {
fmt.Println("Pulling images...")
if err := q.PullImages(version, ""); err != nil {
return err
}
} else {
if err1 := q.InstallQK8s(version, opts); err1 != nil {
return err1
}
if push {
fmt.Println("Pushing images...")
if err := q.PushImagesForCurrentCR(); err != nil {
return err
}
}
return q.InstallQK8s(version, opts, cleanPatchFiles)
}
return AllPostflightChecks(q).Execute()
},
}
eulaPreRunHooks.addValidator(fmt.Sprintf("%v %v", rootCommandName, c.Name()), func(cmd *cobra.Command, q *qliksense.Qliksense) (b bool, err error) {
if filePath != "" {
return loadOrApplyCommandEulaPreRunHook(cmd, q)
} else if qConfig, err := qapi.NewQConfigE(q.QliksenseHome); err != nil {
return false, nil
} else if qcr, err := qConfig.GetCurrentCR(); err != nil {
return false, nil
} else {
return qcr.IsEULA(), nil
}
})
f := c.Flags()
f.StringVarP(&filePath, "file", "f", "", "Install from a CR file")
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.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(&cleanPatchFiles, cleanPatchFilesFlagName, cleanPatchFiles, cleanPatchFilesFlagUsage)
f.StringVarP(&filePath, "file", "f", "", "Install from a CR file")
f.BoolVar(&opts.CleanPatchFiles, cleanPatchFilesFlagName, opts.CleanPatchFiles, cleanPatchFilesFlagUsage)
f.BoolVarP(&opts.Pull, pullFlagName, pullFlagShorthand, opts.Pull, pullFlagUsage)
f.BoolVarP(&opts.Push, pushFlagName, pushFlagShorthand, opts.Push, pushFlagUsage)
f.StringVarP(&opts.AcceptEULA, "acceptEULA", "a", opts.AcceptEULA, "Accept EULA for qliksense")
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(&push, pushFlagName, pushFlagShorthand, push, pushFlagUsage)
return c
}
func validatePullPushFlagsOnInstall(q *qliksense.Qliksense, pull, push bool) error {
if pull && !push {
fmt.Printf("WARNING: pulling images without pushing them")
}
if push {
if err := ensureImageRegistrySetInCR(q); err != nil {
return err
}
}
return nil
}

View File

@@ -1,8 +1,6 @@
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
@@ -20,69 +18,48 @@ func loadCrFile(q *qliksense.Qliksense) *cobra.Command {
Long: `load a CR a file and create necessary structure for future use`,
Example: `qliksense load -f file_name or cat cr_file | qliksense load -f -`,
RunE: func(cmd *cobra.Command, args []string) error {
return runLoadOrApplyCommandE(cmd, func(buffer []byte) error {
return q.LoadCr(bytes.NewReader(buffer), overwriteExistingContext)
})
if crBytes, err := getCrBytesFromFileFlag(cmd); err != nil {
return err
} else {
return q.LoadCr(crBytes, overwriteExistingContext)
}
},
}
f := c.Flags()
f.StringVarP(&filePath, "file", "f", "", "File to load CR from")
c.MarkFlagRequired("file")
f.BoolVarP(&overwriteExistingContext, "overwrite", "o", overwriteExistingContext, "Overwrite any existing contexts with the same name")
eulaPreRunHooks.addValidator(fmt.Sprintf("%v %v", rootCommandName, c.Name()), loadOrApplyCommandEulaPreRunHook)
if err := c.MarkFlagRequired("file"); err != nil {
panic(err)
}
return c
}
func getCrFileFromFlag(cmd *cobra.Command, flagName string) (*os.File, error) {
filePath := cmd.Flag(flagName).Value.String()
if filePath == "-" {
if !isInputFromPipe() {
return nil, errors.New("No input pipe present")
}
return os.Stdin, nil
}
file, e := os.Open(filePath)
if e != nil {
return nil, errors.Wrapf(e,
"unable to read the file %s", filePath)
}
return file, nil
}
func isInputFromPipe() bool {
fileInfo, _ := os.Stdin.Stat()
return fileInfo.Mode()&os.ModeCharDevice == 0
}
func loadOrApplyCommandEulaPreRunHook(cmd *cobra.Command, q *qliksense.Qliksense) (bool, error) {
file, err := getCrFileFromFlag(cmd, "file")
if err != nil {
return false, err
}
defer file.Close()
if crBytes, err := ioutil.ReadAll(file); err != nil {
return false, err
} else {
eulaPreRunHooks.addPostValidationArtifact("CR", crBytes)
return q.IsEulaAcceptedInCrFile(bytes.NewBuffer(crBytes))
}
}
func runLoadOrApplyCommandE(cmd *cobra.Command, callBack func(buffer []byte) error) error {
if crBytes := eulaPreRunHooks.getPostValidationArtifact("CR"); crBytes != nil {
return callBack(crBytes.([]byte))
} else {
file, err := getCrFileFromFlag(cmd, "file")
if err != nil {
return err
}
defer file.Close()
if crBytes, err := ioutil.ReadAll(file); err != nil {
return err
func getCrFileFromFlag(cmd *cobra.Command, flagName string) (*os.File, error) {
filePath := cmd.Flag(flagName).Value.String()
if filePath == "-" {
if !isInputFromPipe() {
return nil, errors.New("No input pipe present")
} else {
return callBack(crBytes)
return os.Stdin, nil
}
} else if file, err := os.Open(filePath); err != nil {
return nil, errors.Wrapf(err, "unable to read the file %s", filePath)
} else {
return file, nil
}
}
func getCrBytesFromFileFlag(cmd *cobra.Command) ([]byte, error) {
if file, err := getCrFileFromFlag(cmd, "file"); err != nil {
return nil, err
} else {
defer file.Close()
return ioutil.ReadAll(file)
}
}

View File

@@ -24,7 +24,7 @@ func postflightCmd(q *qliksense.Qliksense) *cobra.Command {
return postflightCmd
}
func pfMigrationCheck(q *qliksense.Qliksense) *cobra.Command {
func postflightMigrationCheck(q *qliksense.Qliksense) *cobra.Command {
out := ansi.NewColorableStdout()
postflightOpts := &postflight.PostflightOptions{}
var postflightMigrationCmd = &cobra.Command{
@@ -58,3 +58,39 @@ func pfMigrationCheck(q *qliksense.Qliksense) *cobra.Command {
f.BoolVarP(&postflightOpts.Verbose, "verbose", "v", false, "verbose mode")
return postflightMigrationCmd
}
func AllPostflightChecks(q *qliksense.Qliksense) *cobra.Command {
out := ansi.NewColorableStdout()
postflightOpts := &postflight.PostflightOptions{}
var postflightAllChecksCmd = &cobra.Command{
Use: "all",
Short: "perform all checks",
Long: `perform all postflight checks`,
Example: `qliksense postflight all`,
RunE: func(cmd *cobra.Command, args []string) error {
pf := &postflight.QliksensePostflight{Q: q, P: postflightOpts, CG: &api.ClientGoUtils{Verbose: postflightOpts.Verbose}}
// run all postflight checks
fmt.Printf("Running all postflight checks...\n\n")
namespace, kubeConfigContents, err := pf.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Unable to run all postflight checks"))
fmt.Printf("Error: %v\n", err)
return nil
}
if namespace == "" {
namespace = "default"
}
if err = pf.RunAllPostflightChecks(namespace, kubeConfigContents, postflightOpts); err != nil {
fmt.Fprintf(out, "%s\n", Red("1 or more preflight checks have FAILED"))
fmt.Printf("Completed running all postflight checks")
return nil
}
fmt.Fprintf(out, "%s\n", Green("All postflight checks have PASSED"))
return nil
},
}
f := postflightAllChecksCmd.Flags()
f.BoolVarP(&postflightOpts.Verbose, "verbose", "v", false, "verbose mode")
return postflightAllChecksCmd
}

View File

@@ -1,9 +1,6 @@
package main
import (
"errors"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
)
@@ -34,22 +31,8 @@ func pushQliksenseImages(q *qliksense.Qliksense) *cobra.Command {
Short: "Push docker images for offline install",
Example: `qliksense push`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := ensureImageRegistrySetInCR(q); err != nil {
return err
} else {
return q.PushImagesForCurrentCR()
}
return q.PushImagesForCurrentCR()
},
}
return cmd
}
func ensureImageRegistrySetInCR(q *qliksense.Qliksense) error {
qConfig := qapi.NewQConfig(q.QliksenseHome)
if qcr, err := qConfig.GetCurrentCR(); err != nil {
return err
} else if registry := qcr.Spec.GetImageRegistry(); registry == "" {
return errors.New("no image registry set in the CR; to set it use: qliksense config set-image-registry")
}
return nil
}

View File

@@ -2,7 +2,6 @@ package main
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
@@ -106,7 +105,6 @@ func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
Args: cobra.ArbitraryArgs,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if commandUsesContext(cmd.CommandPath()) {
globalEulaPreRun(cmd, p)
if err := p.SetUpQliksenseDefaultContext(); err != nil {
panic(err)
}
@@ -114,25 +112,10 @@ func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
if err := pf.Initialize(); err != nil {
panic(err)
}
globalEulaPostRun(cmd, p)
}
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
if commandUsesContext(cmd.CommandPath()) {
globalEulaPostRun(cmd, p)
}
},
SilenceUsage: true,
}
origHelpFunc := cmd.HelpFunc()
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
if !commandUsesContext(cmd.CommandPath()) {
cmd.Flags().MarkHidden("acceptEULA")
}
origHelpFunc(cmd, args)
})
accept := ""
cmd.PersistentFlags().StringVarP(&accept, "acceptEULA", "a", "", "Accept EULA for qliksense")
cmd.Flags().SetInterspersed(false)
return cmd
}
@@ -234,40 +217,13 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
// add postflight command
postflightCmd := postflightCmd(p)
postflightCmd.AddCommand(pfMigrationCheck(p))
postflightCmd.AddCommand(postflightMigrationCheck(p))
postflightCmd.AddCommand(AllPostflightChecks(p))
cmd.AddCommand(postflightCmd)
return cmd
}
func copy(src, dst string) (int64, error) {
var (
source, destination *os.File
sourceFileStat os.FileInfo
err error
nBytes int64
)
if sourceFileStat, err = os.Stat(src); err != nil {
return 0, err
}
if !sourceFileStat.Mode().IsRegular() {
return 0, fmt.Errorf("%s is not a regular file", src)
}
if source, err = os.Open(src); err != nil {
return 0, err
}
defer source.Close()
if destination, err = os.Create(dst); err != nil {
return 0, err
}
defer destination.Close()
nBytes, err = io.Copy(destination, source)
return nBytes, err
}
func levenstein(cmd *cobra.Command) {
cmd.SuggestionsMinimumDistance = 2
if len(os.Args) > 1 {

View File

@@ -20,6 +20,22 @@ Flags:
-v, --verbose verbose mode
```
### Run all postflight checks
This command runs all the postflight checks available.
```shell
$ qliksense postflight all
Running all postflight checks...
Postflight db migration check...
Logs from pod: qliksense-users-6977cb7788-qlgmv
{"caller":"main.go:39","environment":"qseok","error":"error parsing uri: scheme must be \"mongodb\" or \"mongodb+srv\"","level":"error","message":"failed to connect to ","timestamp":"2020-06-17T04:10:11.7891913Z","version":""}
To view more logs in this context, please run the command: kubectl logs -n test_ns qliksense-users-6977cb7788-qlgmv migration
PASSED
All postflight checks have PASSED
```
### DB migration check
This command checks init containers for successful database migrarion completions, and reports failure, if any to the user.
@@ -29,5 +45,7 @@ An example run of this check produces an output as shown below:
$ qliksense postflight db-migration-check
Logs from pod: qliksense-users-6977cb7788-cxxwh
{"caller":"main.go:39","environment":"qseok","error":"error parsing uri: scheme must be \"mongodb\" or \"mongodb+srv\"","level":"error","message":"failed to connect to ","timestamp":"2020-06-01T01:07:18.4170507Z","version":""}
To view more logs in this context, please run the command: kubectl logs -n test_ns qliksense-users-6977cb7788-qlgmv migration
PASSED
Postflight db_migration_check completed
```

6
go.mod
View File

@@ -10,7 +10,7 @@ replace (
k8s.io/client-go => k8s.io/client-go v0.17.0
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.20200604192606-17370c1af57b
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b
)
require (
@@ -22,7 +22,7 @@ require (
github.com/bugsnag/bugsnag-go v1.5.3 // indirect
github.com/containers/image/v5 v5.1.0
github.com/docker/go-metrics v0.0.1 // indirect
github.com/go-git/go-git/v5 v5.0.0
github.com/go-git/go-git/v5 v5.1.0
github.com/gobuffalo/envy v1.9.0 // indirect
github.com/gobuffalo/logger v1.0.3 // indirect
github.com/gobuffalo/packd v1.0.0 // indirect
@@ -40,7 +40,7 @@ require (
github.com/mitchellh/go-homedir v1.1.0
github.com/otiai10/copy v1.1.1
github.com/pkg/errors v0.9.1
github.com/qlik-oss/k-apis v0.1.7
github.com/qlik-oss/k-apis v0.1.10
github.com/robfig/cron/v3 v3.0.1
github.com/rogpeppe/go-internal v1.5.2 // indirect
github.com/spf13/cobra v0.0.6

22
go.sum
View File

@@ -309,6 +309,8 @@ github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk=
github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
@@ -471,10 +473,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-replayers/grpcreplay v0.1.0 h1:eNb1y9rZFmY4ax45uEEECSa8fsxGRU+8Bil52ASAwic=
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
github.com/google/go-replayers/httpreplay v0.1.0 h1:AX7FUb4BjrrzNvblr/OlgwrmFiep6soj5K2QSDW7BGk=
@@ -624,6 +622,8 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
@@ -885,14 +885,10 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/qlik-oss/k-apis v0.1.5 h1:IeqHuF1IIQCsuSmsUhL7GjdfkOFsNgh3z2UyX59GTsk=
github.com/qlik-oss/k-apis v0.1.5/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
github.com/qlik-oss/k-apis v0.1.6 h1:4rTRwgIN1GtkmLa0VBjvpW2oyVC3h1fOcFHEg3NieNA=
github.com/qlik-oss/k-apis v0.1.6/go.mod h1:r5hXo1mrHOzIdI0Ri9TI4SKjEXft1TZnAyJzOSm9pi0=
github.com/qlik-oss/k-apis v0.7.1 h1:7XHTcIVTSgBM8s9LglWbOeWlnKm84cgHIm7Slrjxb9Y=
github.com/qlik-oss/k-apis v0.7.1/go.mod h1:r5hXo1mrHOzIdI0Ri9TI4SKjEXft1TZnAyJzOSm9pi0=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200604192606-17370c1af57b h1:UqyMR9WA/MbUxCOaaDFk8D2VwM3fG98foDU5nG2HPoM=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200604192606-17370c1af57b/go.mod h1:zh3yFgE5zFk1kreqzVyyj1eXyIxQJT53l4zSg8Wt4SA=
github.com/qlik-oss/k-apis v0.1.10 h1:adBXokJpE7oOr9wkPOHgpVbvuhLLKtqFdnX7V9MEyOs=
github.com/qlik-oss/k-apis v0.1.10/go.mod h1:qJVbbSYtZ+fFCojEyG9UoiCAmymm0JEtnhulr5M7HyU=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b h1:RDh3OZJOriy/ap1NUHVKsPG07N4DALaCzaqXFFK57T0=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b/go.mod h1:zh3yFgE5zFk1kreqzVyyj1eXyIxQJT53l4zSg8Wt4SA=
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/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
@@ -1170,8 +1166,6 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8ou
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=

View File

@@ -5,6 +5,7 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"testing"
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
@@ -60,7 +61,7 @@ func TestCopyDirectory_withGit_withKuz(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
if err := kapis_git.Checkout(repo2, "v0.0.2", "", nil); err != nil {
if err := kapis_git.Checkout(repo2, "v0.0.8", "", nil); err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -69,7 +70,7 @@ func TestCopyDirectory_withGit_withKuz(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
if err := kapis_git.Checkout(repo1, "v0.0.2", "", nil); err != nil {
if err := kapis_git.Checkout(repo1, "v0.0.8", "", nil); err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -78,9 +79,15 @@ func TestCopyDirectory_withGit_withKuz(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
if string(repo2Manifest) != string(repo1Manifest) {
t.Logf("manifest generated on the original config:\n%v", string(repo1Manifest))
t.Logf("manifest generated on the copied config:\n%v", string(repo2Manifest))
re, err := regexp.Compile(`name: qliksense-ca-certificates-[a-z]{5}`)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
repo1ManifestTweaked := re.ReplaceAllString(string(repo1Manifest), "name: qliksense-ca-certificates")
repo2ManifestTweaked := re.ReplaceAllString(string(repo2Manifest), "name: qliksense-ca-certificates")
if repo2ManifestTweaked != repo1ManifestTweaked {
t.Logf("manifest generated on the original config:\n%v", repo1ManifestTweaked)
t.Logf("manifest generated on the copied config:\n%v", repo2ManifestTweaked)
t.Fatal("expected manifests to be equal, but they were not")
}
}

View File

@@ -131,8 +131,8 @@ func (p *PreflightConfig) Initialize() error {
}
p.AddMinK8sV("1.15")
p.AddMinMongoV("3.6")
p.AddImage("nginx", "nginx")
p.AddImage("netcat", "subfuzion/netcat")
p.AddImage("preflight-mongo", "qlik-docker-oss.bintray.io/preflight-mongo")
p.AddImage("nginx", "nginx:1.19.0-alpine")
p.AddImage("netcat", "qlik-docker-oss.bintray.io/preflight-netcat:v1.0.0")
p.AddImage("preflight-mongo", "qlik-docker-oss.bintray.io/preflight-mongo:v1.0.0")
return p.Write()
}

View File

@@ -0,0 +1,31 @@
package postflight
import (
"fmt"
. "github.com/logrusorgru/aurora"
ansi "github.com/mattn/go-colorable"
"github.com/pkg/errors"
)
func (qp *QliksensePostflight) RunAllPostflightChecks(namespace string, kubeConfigContents []byte, preflightOpts *PostflightOptions) error {
checkCount := 0
totalCount := 0
out := ansi.NewColorableStdout()
// Postflight db migration check
if err := qp.DbMigrationCheck(namespace, kubeConfigContents); err != nil {
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n\n", err)
} else {
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
checkCount++
}
totalCount++
if checkCount == totalCount {
// All postflight checks were successful
return nil
}
return errors.New("1 or more postflight checks have FAILED")
}

View File

@@ -11,7 +11,8 @@ import (
const initContainerNameToCheck = "migration"
func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigContents []byte) error {
fmt.Printf("Postflight db migration check... \n")
p.CG.LogVerboseMessage("\n----------------------------------- \n")
clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "")
if err != nil {
err = fmt.Errorf("unable to create a kubernetes client: %v", err)

View File

@@ -95,7 +95,8 @@ func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string,
if sa != "" {
sa = strings.Replace(sa, "name: qliksense", "name: preflight", -1)
} else {
err := fmt.Errorf("Unable to retrieve yamls to apply on cluster from dir: %s, error: %v", kusDir, err)
err = fmt.Errorf(`We were unable to retrieve valid %ss from running "kustomize" in your %s directory.
Please check the value in the "Profile" field of your CR. `, strings.ToLower(entityToTest), kusDir)
return err
}
namespace = "" // namespace is handled when generating the manifests

View File

@@ -1,61 +1,8 @@
package qliksense
import (
"fmt"
"io"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
)
func (q *Qliksense) ApplyCRFromReader(r io.Reader, opts *InstallCommandOptions, cleanPatchFiles, overwriteExistingContext, pull, push bool) error {
if err := q.LoadCr(r, overwriteExistingContext); err != nil {
func (q *Qliksense) ApplyCRFromBytes(crBytes []byte, opts *InstallCommandOptions, overwriteExistingContext bool) error {
if err := q.LoadCr(crBytes, overwriteExistingContext); err != nil {
return err
}
qConfig := qapi.NewQConfig(q.QliksenseHome)
cr, err := qConfig.GetCurrentCR()
if err != nil {
return err
}
version := cr.GetLabelFromCr("version")
if pull {
fmt.Println("Pulling images...")
if err := q.PullImages(version, ""); err != nil {
return err
}
}
if push {
fmt.Println("Pushing images...")
if err := q.PushImagesForCurrentCR(); err != nil {
return err
}
}
if IsQliksenseInstalled(cr.GetName()) {
// it is needed in case want to upgrade from one version to another
if cr.Spec.ManifestsRoot == "" && cr.Spec.Git == nil {
if !qConfig.IsRepoExistForCurrent(version) {
if err := q.FetchQK8s(version); err != nil {
return err
}
}
}
return q.UpgradeQK8s(cleanPatchFiles)
}
return q.InstallQK8s(version, opts, cleanPatchFiles)
}
func IsQliksenseInstalled(crName string) bool {
args := []string{
"get",
"qliksense",
crName,
"-ogo-template",
`--template='{{ .metadata.name}}'`,
}
_, err := qapi.KubectlDirectOps(args, "")
if err != nil {
return false
}
return true
return q.InstallQK8s("", opts)
}

View File

@@ -38,7 +38,11 @@ func unsetAll(qHome string, args []string) error {
}
// delete key inside configs if present
// delete key inside secrets if present
if isRemoved = unsetServiceKey(arg, qcr); !isRemoved {
if isRemoved = unsetServiceKey(arg, qcr); isRemoved {
//return qConfig.WriteCR(qcr)
continue
}
if isRemoved = unsetTopAttrKey(arg, qcr); !isRemoved {
return fmt.Errorf("%s not found in the context", arg)
}
}
@@ -49,7 +53,7 @@ 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("")
v.Set(reflect.Zero(v.Type()))
return true
}
return false
@@ -99,6 +103,21 @@ func unsetServiceKey(svcKey string, qcr *api.QliksenseCR) bool {
return false
}
func unsetTopAttrKey(attKey string, qcr *api.QliksenseCR) bool {
sk := strings.Split(attKey, ".")
attStruct := sk[0]
key := sk[1]
attV := reflect.ValueOf(qcr.Spec).Elem().FieldByName(strings.Title(attStruct))
if !attV.IsValid() || attV.IsZero() || attV.IsNil() {
return false
}
v := attV.Elem().FieldByName(strings.Title(key))
if v.IsValid() && v.CanSet() {
v.Set(reflect.Zero(v.Type()))
return true
}
return false
}
func findIndex(elem string, nvs kconfig.NameValues) int {
for i, nv := range nvs {
if nv.Name == elem {

View File

@@ -16,7 +16,9 @@ func TestUnsetAll(t *testing.T) {
testPepareDir(qHome)
defer os.RemoveAll(qHome)
//fmt.Print(qHome)
args := []string{"rotateKeys", "qliksense", "qliksense2.acceptEula3", "serviceA.acceptEula"}
args := []string{"rotateKeys", "qliksense", "qliksense2.acceptEula3", "serviceA.acceptEula", "opsRunner.watchBranch"}
//args := []string{"opsRunner"}
//args := []string{"opsRunner.watchBranch"}
if err := unsetAll(qHome, args); err != nil {
t.Log("error during unset", err)
t.FailNow()
@@ -44,6 +46,14 @@ func TestUnsetAll(t *testing.T) {
t.Log("serviceA not deleted")
t.Fail()
}
if qcr.Spec.OpsRunner == nil {
t.Log("opsRunner not deleted")
t.Fail()
}
if qcr.Spec.OpsRunner.WatchBranch != "" {
t.Log("opsRunner.watchBranch not deleted")
t.Fail()
}
}
func testPepareDir(qHome string) {
@@ -73,6 +83,9 @@ metadata:
spec:
profile: docker-desktop
rotateKeys: "yes"
opsRunner:
enabled: "yes"
watchBranch: something
configs:
qliksense:
- name: acceptEula

View File

@@ -30,19 +30,14 @@ const (
func (q *Qliksense) PullImages(version, profile string) error {
qConfig := qapi.NewQConfig(q.QliksenseHome)
if version != "" {
if !qConfig.IsRepoExistForCurrent(version) {
if err := q.FetchQK8s(version); err != nil {
return err
}
}
}
qcr, err := qConfig.GetCurrentCR()
if err != nil {
return err
}
if !qcr.IsRepoExist() {
return errors.New("ManifestsRoot not found")
if err := fetchAndUpdateCR(qConfig, version); err != nil {
return err
}
}
if profile != "" {
qcr.Spec.Profile = profile
@@ -160,7 +155,10 @@ func (q *Qliksense) PushImagesForCurrentCR() error {
qcr, err := qConfig.GetCurrentCR()
if err != nil {
return err
} else if err := ensureImageRegistrySetInCR(qcr); err != nil {
return err
}
version := qcr.GetLabelFromCr("version")
profile := qcr.Spec.Profile
repoDir := qcr.Spec.ManifestsRoot
@@ -344,3 +342,20 @@ func (q *Qliksense) writeVersionOutput(versionOut *VersionOutput, imagesDir, ver
}
return nil
}
func validatePullPushFlagsOnInstall(cr *qapi.QliksenseCR, pull, push bool) error {
if pull && !push {
fmt.Printf("WARNING: pulling images without pushing them\n")
}
if push {
return ensureImageRegistrySetInCR(cr)
}
return nil
}
func ensureImageRegistrySetInCR(cr *qapi.QliksenseCR) error {
if registry := cr.Spec.GetImageRegistry(); registry == "" {
return errors.New("no image registry set in the CR; to set it use: qliksense config set-image-registry")
}
return nil
}

View File

@@ -235,17 +235,17 @@ spec:
t.Fatal("expected to find the GitOps image in the list, but it wasn't there")
}
if !haveMatchingImage(func(image string) bool {
return image == "nginx"
return strings.Contains(image, "nginx")
}) {
t.Fatal("expected to find the nginx Preflight image in the list, but it wasn't there")
}
if !haveMatchingImage(func(image string) bool {
return image == "subfuzion/netcat"
return strings.Contains(image, "preflight-netcat")
}) {
t.Fatal("expected to find the netcat Preflight image in the list, but it wasn't there")
}
if !haveMatchingImage(func(image string) bool {
return image == "qlik-docker-oss.bintray.io/preflight-mongo"
return strings.Contains(image, "qlik-docker-oss.bintray.io/preflight-mongo")
}) {
t.Fatal("expected to find the preflight-mongo image in the list, but it wasn't there")
}

View File

@@ -16,7 +16,7 @@ func TestFetchAndUpdateCR(t *testing.T) {
}
q.SetUpQliksenseContext("test1")
qConfig := qapi.NewQConfig(tempHome)
if err := fetchAndUpdateCR(qConfig, "v0.0.2"); err != nil {
if err := fetchAndUpdateCR(qConfig, "v0.0.8"); err != nil {
t.Log(err)
t.FailNow()
}
@@ -28,8 +28,8 @@ func TestFetchAndUpdateCR(t *testing.T) {
t.FailNow()
}
if cr.Spec.ManifestsRoot != "contexts/test1/qlik-k8s/v0.0.2" {
t.Log("actual path: " + cr.Spec.ManifestsRoot + ", expected path: contexts/test1/qlik-k8s/v0.0.2")
if cr.Spec.ManifestsRoot != "contexts/test1/qlik-k8s/v0.0.8" {
t.Log("actual path: " + cr.Spec.ManifestsRoot + ", expected path: contexts/test1/qlik-k8s/v0.0.8")
t.FailNow()
}
//testing latest tag is fetched
@@ -43,7 +43,7 @@ func TestFetchAndUpdateCR(t *testing.T) {
cr = &qapi.QliksenseCR{}
qapi.ReadFromFile(cr, actualCrFile)
v := cr.GetLabelFromCr("version")
if v == "" || v == "v0.0.2" {
if v == "" || v == "v0.0.8" {
t.Log("should get latest but got version: " + v)
t.Fail()
}

View File

@@ -7,6 +7,9 @@ import (
"os"
"path"
"path/filepath"
"strings"
"github.com/mattn/go-tty"
"github.com/mitchellh/go-homedir"
"github.com/qlik-oss/k-apis/pkg/config"
@@ -17,34 +20,38 @@ import (
)
type InstallCommandOptions struct {
StorageClass string
MongodbUri string
RotateKeys string
DryRun bool
StorageClass string
MongodbUri string
RotateKeys string
AcceptEULA string
DryRun bool
Pull bool
Push bool
CleanPatchFiles bool
}
func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, cleanPatchFiles bool) error {
const (
eulaText = "Please read the end user license agreement at: https://www.qlik.com/us/legal/license-terms"
eulaPrompt = "Do you accept our EULA? (y/n): "
eulaErrorInstruction = `You must enter "y" to continue or execute the command with the acceptEULA flag set to "yes"`
)
// step1: fetch 1.0.0 # pull down qliksense-k8s@1.0.0
// step2: operator view | kubectl apply -f # operator manifest (CRD)
// step3: config apply | kubectl apply -f # generates patches (if required) in configuration directory, applies manifest
// step4: config view | kubectl apply -f # generates Custom Resource manifest (CR)
func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions) error {
// fetch the version
qConfig := qapi.NewQConfig(q.QliksenseHome)
if cleanPatchFiles {
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
fmt.Printf("error removing temporary changes to the config: %v\n", err)
}
}
qcr, err := qConfig.GetCurrentCR()
if err != nil {
fmt.Println("cannot get the current-context cr", err)
return err
}
if opts.AcceptEULA != "" && opts.AcceptEULA != "yes" {
enforceEula()
} else if opts.AcceptEULA == "" && !qcr.IsEULA() {
enforceEula()
}
qcr.SetEULA("yes")
if opts.MongodbUri != "" {
qcr.Spec.AddToSecrets("qliksense", "mongodbUri", opts.MongodbUri, "")
}
@@ -54,6 +61,13 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, cle
if opts.RotateKeys != "" {
qcr.Spec.RotateKeys = opts.RotateKeys
}
if opts.CleanPatchFiles {
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
fmt.Printf("error removing temporary changes to the config: %v\n", err)
}
}
// for debugging purpose
if opts.DryRun {
// generate patches
@@ -72,6 +86,22 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, cle
return errors.New(`please install CRDs by executing: $ qliksense crds install`)
}
if err := validatePullPushFlagsOnInstall(qcr, opts.Pull, opts.Push); err != nil {
return err
}
if opts.Pull {
fmt.Println("Pulling images...")
if err := q.PullImages(version, ""); err != nil {
return err
}
}
if opts.Push {
fmt.Println("Pushing images...")
if err := q.PushImagesForCurrentCR(); err != nil {
return err
}
}
if err := applyImagePullSecret(qConfig); err != nil {
return err
}
@@ -108,11 +138,7 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, cle
}
}
qcr, err = qConfig.GetCurrentCR()
if err != nil {
fmt.Println("cannot get the current-context cr", err)
return err
} else if qcr.Spec.GetManifestsRoot() == "" {
if qcr.Spec.GetManifestsRoot() == "" {
return errors.New("cannot get the manifest root. Use qliksense fetch <version> or qliksense set manifestsRoot")
}
@@ -123,7 +149,7 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, cle
return err
} else {
if IsQliksenseInstalled(dcr.GetName()) {
return q.UpgradeQK8s(cleanPatchFiles)
return q.UpgradeQK8s(opts.CleanPatchFiles)
}
if err := q.applyConfigToK8s(dcr); err != nil {
fmt.Println("cannot do kubectl apply on manifests")
@@ -134,6 +160,21 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, cle
}
}
func IsQliksenseInstalled(crName string) bool {
args := []string{
"get",
"qliksense",
crName,
"-ogo-template",
`--template='{{ .metadata.name}}'`,
}
_, err := qapi.KubectlDirectOps(args, "")
if err != nil {
return false
}
return true
}
func (q *Qliksense) getProcessedOperatorControllerString(qcr *qapi.QliksenseCR) (string, error) {
operatorControllerString := q.GetOperatorControllerString()
if imageRegistry := qcr.Spec.GetImageRegistry(); imageRegistry != "" {
@@ -226,3 +267,26 @@ func (q *Qliksense) createK8sResourceBeforePatch(qcr *qapi.QliksenseCR) error {
func isK8sSecretNeedToCreate(nv config.NameValue) bool {
return nv.ValueFrom != nil
}
func enforceEula() {
fmt.Println(eulaText)
fmt.Print(eulaPrompt)
answer := readAnswerFromTty()
if strings.ToLower(answer) != "y" {
fmt.Println(eulaErrorInstruction)
os.Exit(1)
}
}
func readAnswerFromTty() string {
t, err := tty.Open()
if err != nil {
panic(err)
}
defer t.Close()
answer, err := t.ReadString()
if err != nil {
panic(err)
}
return answer
}

View File

@@ -5,7 +5,6 @@ import (
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"testing"
@@ -48,15 +47,8 @@ spec:
profile: docker-desktop
rotateKeys: "yes"`
crFile := filepath.Join(testDir, "install_test.yaml")
ioutil.WriteFile(crFile, []byte(sampleCr), 0644)
q := New(testDir)
file, e := os.Open(crFile)
if e != nil {
t.Log(e)
t.FailNow()
}
if err := q.LoadCr(file, false); err != nil {
if err := q.LoadCr([]byte(sampleCr), false); err != nil {
t.Log(err)
t.FailNow()
}

View File

@@ -3,17 +3,13 @@ package qliksense
import (
"errors"
"fmt"
"io"
"io/ioutil"
"strings"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
)
func (q *Qliksense) LoadCr(reader io.Reader, overwriteExistingContext bool) error {
if crBytes, err := ioutil.ReadAll(reader); err != nil {
return err
} else if crName, err := q.loadCrStringIntoFileSystem(string(crBytes), overwriteExistingContext); err != nil {
func (q *Qliksense) LoadCr(crBytes []byte, overwriteExistingContext bool) error {
if crName, err := q.loadCrStringIntoFileSystem(string(crBytes), overwriteExistingContext); err != nil {
return err
} else {
fmt.Println("cr name: [ " + crName + " ] has been loaded")
@@ -21,16 +17,6 @@ func (q *Qliksense) LoadCr(reader io.Reader, overwriteExistingContext bool) erro
return nil
}
func (q *Qliksense) IsEulaAcceptedInCrFile(reader io.Reader) (bool, error) {
if crBytes, err := ioutil.ReadAll(reader); err != nil {
return false, err
} else if cr, err := qapi.CreateCRObjectFromString(string(crBytes)); err != nil {
return false, err
} else {
return cr.IsEULA(), nil
}
}
func (q *Qliksense) loadCrStringIntoFileSystem(crstr string, overwriteExistingContext bool) (string, error) {
cr, err := qapi.CreateCRObjectFromString(crstr)
if err != nil {

View File

@@ -1,9 +1,6 @@
package qliksense
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
@@ -79,30 +76,13 @@ spec:
repository: https://github.com/ffoysal/qliksense-k8s
accessToken: abababababababaab
userName: "blblbl"`
crFile1 := filepath.Join(testDir, "testcr1.yaml")
ioutil.WriteFile(crFile1, []byte(sampleCr1), 0644)
crFile2 := filepath.Join(testDir, "testcr2.yaml")
ioutil.WriteFile(crFile2, []byte(sampleCr2), 0644)
dupCrFile := filepath.Join(testDir, "dupcr.yaml")
ioutil.WriteFile(dupCrFile, []byte(duplicateCr), 0644)
q := New(testDir)
file1, e := os.Open(crFile1)
if e != nil {
t.Log(e)
t.FailNow()
}
if err := q.LoadCr(file1, false); err != nil {
if err := q.LoadCr([]byte(sampleCr1), false); err != nil {
t.Log(err)
t.FailNow()
}
file2, e := os.Open(crFile2)
if e != nil {
t.Log(e)
t.FailNow()
}
if err := q.LoadCr(file2, false); err != nil {
if err := q.LoadCr([]byte(sampleCr2), false); err != nil {
t.Log(err)
t.FailNow()
}
@@ -128,12 +108,7 @@ spec:
if qConfig.Spec.CurrentContext != "qlik-test3" {
t.FailNow()
}
file, e := os.Open(dupCrFile)
if e != nil {
t.Log(e)
t.FailNow()
}
if err := q.LoadCr(file, false); err == nil {
if err := q.LoadCr([]byte(duplicateCr), false); err == nil {
t.FailNow()
}
td()