Compare commits
11 Commits
upgrade-ka
...
autorun_po
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33ab6c632b | ||
|
|
6899a7be77 | ||
|
|
d3f33507d0 | ||
|
|
a619970385 | ||
|
|
293e923c82 | ||
|
|
504190c75c | ||
|
|
fc62e5fea4 | ||
|
|
bf4e4b9bf1 | ||
|
|
4cb3231a08 | ||
|
|
e1dbcfaac8 | ||
|
|
77a3bf4581 |
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
6
go.mod
@@ -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
22
go.sum
@@ -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=
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
31
pkg/postflight/all_postflight_checks.go
Normal file
31
pkg/postflight/all_postflight_checks.go
Normal 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")
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user