Compare commits
29 Commits
v0.29.0
...
update-kus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbfee1eee6 | ||
|
|
6ab9317638 | ||
|
|
5899760c16 | ||
|
|
a63c400106 | ||
|
|
568012edd8 | ||
|
|
34f702b183 | ||
|
|
d8cdbb30bb | ||
|
|
616e759089 | ||
|
|
c2d3f39542 | ||
|
|
0b6c1b8bc9 | ||
|
|
7091da099c | ||
|
|
1f8bcf3469 | ||
|
|
627a4a14f9 | ||
|
|
66333e9e97 | ||
|
|
36c91e9dab | ||
|
|
b6ae0c9873 | ||
|
|
6899a7be77 | ||
|
|
373d6499dc | ||
|
|
746823c54b | ||
|
|
73b5da8c14 | ||
|
|
7cc6e55779 | ||
|
|
27c8f3ee8e | ||
|
|
cb9f463f01 | ||
|
|
293e923c82 | ||
|
|
504190c75c | ||
|
|
fc62e5fea4 | ||
|
|
bf4e4b9bf1 | ||
|
|
4cb3231a08 | ||
|
|
e1dbcfaac8 |
2
.github/workflows/mkdocs.yml
vendored
2
.github/workflows/mkdocs.yml
vendored
@@ -16,6 +16,6 @@ jobs:
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Deploy docs
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@1.11
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@1.12
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -19,7 +19,7 @@ func about(q *qliksense.Qliksense) *cobra.Command {
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: "about ref",
|
||||
Short: "Displays information pertaining to Qliksense on Kubernetes",
|
||||
Short: "Displays information pertaining to qliksense on Kubernetes",
|
||||
Long: "Gives the version of QLik Sense on Kubernetes and versions of images.",
|
||||
Example: `
|
||||
qliksense about 1.0.0
|
||||
|
||||
@@ -1,74 +1,44 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ func crdsInstallCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
}
|
||||
c := &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "Install CRDs for Qliksense application. Use install --all=false to exclude the operator CRD",
|
||||
Long: "Install CRDs for Qliksense application. Use install --all=false to exclude the operator CRD",
|
||||
Short: "Install CRDs for qliksense application. Use install --all=false to exclude the operator CRD",
|
||||
Long: "Install CRDs for qliksense application. Use install --all=false to exclude the operator CRD",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return q.InstallCrds(opts)
|
||||
},
|
||||
|
||||
@@ -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,35 @@ 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 err := q.InstallQK8s(version, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if push {
|
||||
fmt.Println("Pushing images...")
|
||||
if err := q.PushImagesForCurrentCR(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return q.InstallQK8s(version, opts, cleanPatchFiles)
|
||||
}
|
||||
postflightChecksCmd := AllPostflightChecks(q)
|
||||
postflightChecksCmd.DisableFlagParsing = true
|
||||
return postflightChecksCmd.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
|
||||
}
|
||||
|
||||
31
cmd/qliksense/keys.go
Normal file
31
cmd/qliksense/keys.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var keysCmd = &cobra.Command{
|
||||
Use: "keys",
|
||||
Short: "keys for qliksense",
|
||||
}
|
||||
|
||||
func keysRotateCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "rotate",
|
||||
Short: "Rotate qliksense application keys",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := q.InstallQK8s("", &qliksense.InstallCommandOptions{
|
||||
CleanPatchFiles: true,
|
||||
RotateKeys: true,
|
||||
}); err != nil {
|
||||
return err
|
||||
} else {
|
||||
postFlightChecksCmd := AllPostflightChecks(q)
|
||||
postFlightChecksCmd.DisableFlagParsing = true
|
||||
return postFlightChecksCmd.Execute()
|
||||
}
|
||||
},
|
||||
}
|
||||
return c
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -472,3 +472,42 @@ func pfCleanupCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return pfCleanCmd
|
||||
}
|
||||
|
||||
func pfVerifyCAChainCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
|
||||
var pfVerifyCAChainCmd = &cobra.Command{
|
||||
Use: "verify-ca-chain",
|
||||
Short: "verify-ca-chain using openssl verify",
|
||||
Long: `verify the CA chain using openssl verify to ensure that mongodb certificate is valid`,
|
||||
Example: `qliksense preflight verify-ca-chain`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||
|
||||
// Preflight service check
|
||||
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
if err = qp.VerifyCAChain(kubeConfigContents, namespace, preflightOpts, false); err != nil {
|
||||
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := pfVerifyCAChainCmd.Flags()
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return pfVerifyCAChainCmd
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -29,10 +28,10 @@ const (
|
||||
cleanPatchFilesFlagUsage = "Set --clean=false to keep any prior config repo file changes on install (for debugging)"
|
||||
pullFlagName = "pull"
|
||||
pullFlagShorthand = "d"
|
||||
pullFlagUsage = "If using private docker registry, pull (download) all required Qliksense images before install"
|
||||
pullFlagUsage = "If using private docker registry, pull (download) all required qliksense images before install"
|
||||
pushFlagName = "push"
|
||||
pushFlagShorthand = "u"
|
||||
pushFlagUsage = "If using private docker registry, push (upload) all downloaded Qliksense images to that registry before install"
|
||||
pushFlagUsage = "If using private docker registry, push (upload) all downloaded qliksense images to that registry before install"
|
||||
rootCommandName = "qliksense"
|
||||
)
|
||||
|
||||
@@ -101,12 +100,11 @@ func commandUsesContext(commandName string) bool {
|
||||
func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: rootCommandName,
|
||||
Short: "Qliksense cli tool",
|
||||
Short: "qliksense cli tool",
|
||||
Long: `qliksense cli tool provides functionality to perform operations on qliksense-k8s, qliksense operator, and kubernetes cluster`,
|
||||
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
|
||||
}
|
||||
@@ -226,6 +209,7 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
preflightCmd.AddCommand(pfCreateRoleBindingCheckCmd(p))
|
||||
preflightCmd.AddCommand(pfCreateServiceAccountCheckCmd(p))
|
||||
preflightCmd.AddCommand(pfCreateAuthCheckCmd(p))
|
||||
preflightCmd.AddCommand(pfVerifyCAChainCmd(p))
|
||||
preflightCmd.AddCommand(pfCleanupCmd(p))
|
||||
|
||||
cmd.AddCommand(preflightCmd)
|
||||
@@ -234,40 +218,17 @@ 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)
|
||||
|
||||
// add keys command
|
||||
cmd.AddCommand(keysCmd)
|
||||
keysCmd.AddCommand(keysRotateCmd(p))
|
||||
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 {
|
||||
|
||||
@@ -74,7 +74,6 @@ spec:
|
||||
- name: mongodbUri
|
||||
value: mongodb://qlik-test-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
```
|
||||
|
||||
`qliksense apply` does everything `qliksense load` does but will install Qlik Sense into the cluster as well
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# How CLI works
|
||||
|
||||
At the initialization, `qliksense` cli creates few files in the director `~/.qliksene` and it contains following files:
|
||||
At the initialization, `qliksense` cli creates few files in the director `~/.qliksense` and it contains following files:
|
||||
|
||||
```console
|
||||
.qliksense
|
||||
@@ -25,7 +25,6 @@ spec:
|
||||
qliksense:
|
||||
- name: mongodbUri
|
||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik-default
|
||||
```
|
||||
|
||||
|
||||
@@ -1,53 +1,162 @@
|
||||
# Getting started
|
||||
|
||||
To get familiar with the Qlik Sense on Kubernetes Operator Command Line Interface (CLI), we will install Qlik Sense on Kubernetes on docker desktop. In subsequent sections we will enhance this configuration to include an Identity Provider (keycloak) and demonstrate air gapped capabilities as well.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Kubernetes cluster (Docker Desktop with enabled Kubernetes)
|
||||
- `kubectl` installed, configured and able to communicate with kubernetes cluster. _`qliksense` CLI uses `kubectl` under the hood to perform operations on cluster_
|
||||
- `kubectl` installed, configured and able to communicate with kubernetes cluster. _`qliksense` CLI uses `kubectl` to perform some operations on cluster_
|
||||
|
||||
## Installing `qliksense` CLI
|
||||
|
||||
Download the executable for your platform from [releases page](https://github.com/qlik-oss/sense-installer/releases) and rename it to `qliksense`
|
||||
|
||||
??? tldr "Linux"
|
||||
=== "Linux"
|
||||
|
||||
``` bash
|
||||
curl -Lo qliksense https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-linux-amd64
|
||||
chmod +x qliksense
|
||||
sudo mv qliksense /usr/local/bin
|
||||
# bash
|
||||
|
||||
curl -LOJ https://storage.googleapis.com/kubernetes-release/release/v1.16.8/bin/linux/amd64/kubectl
|
||||
curl -LOJ https://github.com/qlik-oss/sense-installer/releases/latest/download/qliksense-linux-amd64
|
||||
sudo mv qliksense-linux-amd64 kubectl /usr/local/bin
|
||||
sudo chmod ugo+x /usr/local/bin/qliksense-linux-amd64 /usr/local/bin/kubectl
|
||||
sudo ln -s /usr/local/bin/qliksense-linux-amd64 /usr/local/bin/qliksense
|
||||
sudo ln -s /usr/local/bin/qliksense-linux-amd64 /usr/local/bin/kubectl-qliksense
|
||||
```
|
||||
|
||||
??? tldr "MacOS"
|
||||
=== "MacOS"
|
||||
|
||||
``` bash
|
||||
curl -Lo qliksense https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-darwin-amd64
|
||||
chmod +x qliksense
|
||||
sudo mv qliksense /usr/local/bin
|
||||
# bash
|
||||
|
||||
curl -LOJ https://storage.googleapis.com/kubernetes-release/release/v1.16.8/bin/darwin/amd64/kubectl
|
||||
curl -LOJ https://github.com/qlik-oss/sense-installer/releases/latest/download/qliksense-darwin-amd64
|
||||
sudo mv qliksense-darwin-amd64 kubectl /usr/local/bin
|
||||
sudo chmod ugo+x /usr/local/bin/qliksense-darwin-amd64 /usr/local/bin/kubectl
|
||||
sudo ln -s /usr/local/bin/qliksense-darwin-amd64 /usr/local/bin/qliksense
|
||||
sudo ln -s /usr/local/bin/qliksense-darwin-amd64 /usr/local/bin/kubectl-qliksense
|
||||
```
|
||||
|
||||
??? tldr "Windows"
|
||||
Download Windows executable and add it in your `PATH` as `qliksense.exe`
|
||||
=== "Windows"
|
||||
|
||||
[https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-windows-amd64.exe](https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-windows-amd64.exe)
|
||||
|
||||
``` powershell
|
||||
# powershell
|
||||
|
||||
Invoke-WebRequest https://storage.googleapis.com/kubernetes-release/release/v1.16.8/bin/windows/amd64/kubectl.exe -O C:\bin\kubectl.exe
|
||||
Invoke-WebRequest https://github.com/qlik-oss/sense-installer/releases/latest/download/qliksense-windows-amd64.exe -O C:\bin\qliksense.exe
|
||||
Copy-Item C:\bin\qliksense.exe C:\bin\kubectl-qliksense.exe
|
||||
# Add C:\bin to current Path
|
||||
$Env:Path += ";C:\bin"
|
||||
# Save Path to User environment scope
|
||||
[Environment]::SetEnvironmentVariable("Path",[Environment]::GetEnvironmentVariable("Path", [EnvironmentVariableTarget]::User) + ";C:\bin",[EnvironmentVariableTarget]::User)
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
- To download the version `v0.0.2` from qliksense-k8s [releases](https://github.com/qlik-oss/qliksense-k8s/releases).
|
||||
### Setting the contexts
|
||||
|
||||
By default a `qlik-default` configuration context is provided and can be used, as is. In effect, this is the name of the Qlik Sense instance in the target cluster. All resources installed into the target namespace will be prefixed with `qlik-default`. The name of the Qlik Sense application will correspondingly be `qliksense`.
|
||||
|
||||
Ex.: To change this to `qliksense-dev`:
|
||||
|
||||
```shell
|
||||
qliksense fetch v0.0.2
|
||||
qliksense config set-context qliksense-dev
|
||||
```
|
||||
|
||||
- To install CRDs for QSEoK and qliksense operator into the kubernetes cluster.
|
||||
!!! info ""
|
||||
For the purposes of the Quick Start we will be using `qlik-default`
|
||||
|
||||
The target namespace is determined by the kubectl connection context.
|
||||
|
||||
ex. Ensure a connection to cluster to change the configuration context's target namespace with kubectl to `qliksense`
|
||||
|
||||
```shell
|
||||
qliksense crds install --all
|
||||
kubectl config set-context --current --namespace=qliksense
|
||||
```
|
||||
|
||||
- To install QSEoK into a namespace in the kubernetes cluster where `kubectl` is pointing to.
|
||||
!!! info ""
|
||||
For the purposes of the Quick Start we will be using the default namespace. (`default`)
|
||||
|
||||
### Downloading a version of Qlik Sense on Kubernetes
|
||||
|
||||
To download the latest version of Qlik Sense on Kubernetes from qliksense-k8s
|
||||
|
||||
```shell
|
||||
qliksense install --acceptEULA="yes"
|
||||
qliksense fetch
|
||||
```
|
||||
|
||||
#### More Options
|
||||
|
||||
- To download a specific version `v1.59.20` from qliksense-k8s [releases](https://github.com/qlik-oss/qliksense-k8s/releases)
|
||||
```shell
|
||||
qliksense fetch v1.58.20
|
||||
```
|
||||
- To download from a GitHub repository fork of the `qliksense-k8s` repository (master branch)
|
||||
```shell
|
||||
qliksense fetch --url https://github.com/bkuschel/qliksense-k8s.git master
|
||||
```
|
||||
|
||||
### Deployment Profiles
|
||||
|
||||
Deployment profiles are a sets components that require sets of key/value pairs to satisfy the requirements for the generation of a Qlik Sense on Kubernetes manifest. Along with the profile name, sets of key/value pairs are provided through the Qlik Sense custom application resources (see here).
|
||||
|
||||
Profiles can be developed and added to the qliksense-k8s repo but is considered an advanced topic (see here) not covered here.
|
||||
|
||||
#### Default Profile: Docker Desktop
|
||||
|
||||
By default, the `docker-desktop` profile is associated with the configuration context when initially created. This profile is guaranteed to work on Docker Desktop but can generally be used on other types of Kubernetes clusters, provided that the required configuration tweaks are provided specific to the hosting requirements (Ex. storage class).
|
||||
|
||||
The docker-desktop profile does not have any scaling characteristics and is generally set up to have the ability to work on a reasonably powerful computer (16GB, 4 cores minimum, greater is better). It also includes a self-contained mongodb instance for non-production purposes.
|
||||
|
||||
Generally it doesn't require any extra configuration to work except an acceptance of the Qlik User License Agreement (QULA), which is prompted on install but can also be set in advance (having read the QULA)
|
||||
|
||||
```shell
|
||||
qliksense config set-configs qliksense.acceptEULA="yes"
|
||||
```
|
||||
|
||||
More information on the possible configuration parameters for docker-desktop here (see here).
|
||||
|
||||
!!! Info
|
||||
To access an installation of the docker desktop profile in docker desktop, the host `elastic.example` needs to be added to the system host file as an alias to `127.0.0.1`
|
||||
|
||||
```
|
||||
127.0.0.1 elastic.example
|
||||
```
|
||||
|
||||
File location:
|
||||
|
||||
- Linux - `/etc/hosts`
|
||||
- MacOS - `/etc/hosts`
|
||||
- Windows - `C:\Windows\System32\drivers\etc\hosts`
|
||||
|
||||
### Installing Qlik Sense on Kubernetes
|
||||
|
||||
#### Custom Resource Definitions (CRDs)
|
||||
|
||||
Besides the CLI, a Kubernetes operator (read here) is a core component of the Qlik Sense Operator. Additionally, there are other Kubernetes operators in Qlik Sense on Kubernetes that provide other types functionality (ex. scaling). Depending on the profile chosen [(see Deployment profiles)](#deployment-profiles), additional CRDs can also be installed for third-party components (see gke-demo).
|
||||
|
||||
Kubernetes operators require Custom resource definitions (CRD) (read here), which are YAML schemas for custom resources (CR). The Qlik Sense application instance, corresponding to the name of the configuration context, corresponds to a CR (ex. `qlik-default`).
|
||||
|
||||
CRDs require cluster scope permissions and are shared cluster-wide across namespaces. These need to be installed first (if not done previously).
|
||||
|
||||
To install CRDs for Qlik Sense on Kubernetes into the Kubernetes cluster.
|
||||
|
||||
```shell
|
||||
qliksense crds install
|
||||
```
|
||||
|
||||
#### Preflight Checks
|
||||
|
||||
To check that your environment fullfills Qlik Sense requirements
|
||||
|
||||
```shell
|
||||
qliksense preflight all
|
||||
```
|
||||
|
||||
#### Qlik Sense
|
||||
|
||||
To install Qlik Sense into a namespace in the Kubernetes cluster where `kubectl` is pointing to
|
||||
|
||||
```shell
|
||||
qliksense install
|
||||
```
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
# Overview
|
||||
|
||||
The Qlik Sense on Kubernetes CLI (`qliksense`) provides an imperative interface to many of the configurations that need to be applied against the declarative structure described in [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s).
|
||||
|
||||
The CLI facilitates:
|
||||
The Qlik Sense on Kubernetes Operator CLI (`qliksense`) facilitates:
|
||||
|
||||
- Installation of QSEoK
|
||||
- Installation of qliksense operator to manage QSEoK
|
||||
- Installation of Qliksense operator to manage the QSEoK installation
|
||||
- Air gapped installation of QSEoK
|
||||
- Cluster configuration management
|
||||
- Pre-flight and Post-flight environment and configuration checks
|
||||
|
||||
The Qlik Sense on Kubernetes Operator CLI provides an imperative interface to many of the configurations that need to be applied against the declarative structure described in the [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s) repository
|
||||
|
||||
To get start quickly go to the [Getting Started page](getting_started.md).
|
||||
|
||||
To learn more about the internal workings of the Qlik Sense on Kubernetes Operator, go to [How CLI works](concepts.md).
|
||||
|
||||
!!! info ""
|
||||
This is a technology preview that uses Qlik modified [kustomize](https://github.com/qlik-oss/kustomize) for Kubernetes manifests on [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s) repository
|
||||
|
||||
|
||||
!!! info ""
|
||||
See QlikSense [edge releases on qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s/releases) repository
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -305,3 +305,21 @@ Removing mongo check components...
|
||||
Preflight cleanup complete
|
||||
|
||||
```
|
||||
|
||||
### Verify-ca-chain check
|
||||
We use the command below to verify the ca certificate chain and server certificate. We run this check over mongodbUrl and discoveryUrl we inferred from idpconfigs in the CR.
|
||||
```shell
|
||||
$ qliksense preflight preflight verify-ca-chain -v
|
||||
|
||||
Preflight verify-ca-chain check...
|
||||
-----------------------------------
|
||||
Openssl verify mongodbUrl:
|
||||
Mongodb url inferred form CR: <mongodbUrl_from_CR>
|
||||
Host: <host extracted from mongodbUrl>
|
||||
|
||||
Openssl verify discoveryUrl:
|
||||
Discovery url: <discoveryUrl_from_CR>
|
||||
Host: <host extracted from discoveryUrl>
|
||||
Completed preflight verify-CA-chain check
|
||||
PASSED
|
||||
```
|
||||
13
go.mod
13
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.5.2-0.20200820111149-1a59db58525f
|
||||
)
|
||||
|
||||
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,23 +40,24 @@ 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.16
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
||||
github.com/spf13/cobra v0.0.6
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/viper v1.6.1
|
||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 // indirect
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a // indirect
|
||||
golang.org/x/net v0.0.0-20200528225125-3c3fba18258b
|
||||
golang.org/x/tools v0.0.0-20200312194400-c312e98713c2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
k8s.io/api v0.17.2
|
||||
k8s.io/apiextensions-apiserver v0.17.2
|
||||
k8s.io/apimachinery v0.17.2
|
||||
k8s.io/client-go v11.0.0+incompatible
|
||||
k8s.io/kubectl v0.17.2
|
||||
sigs.k8s.io/kustomize/api v0.3.2
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
exclude github.com/Azure/go-autorest v12.0.0+incompatible
|
||||
|
||||
61
go.sum
61
go.sum
@@ -32,6 +32,7 @@ contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcig
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg=
|
||||
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0=
|
||||
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
|
||||
github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU=
|
||||
github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo=
|
||||
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
||||
@@ -84,6 +85,7 @@ github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEY
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
|
||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
@@ -106,6 +108,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6 h1:bZ28Hqta7TFAK3Q08CMvv8y3/8ATaEqv2nGoc6yff6c=
|
||||
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U=
|
||||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
@@ -123,6 +126,7 @@ github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||
github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
@@ -161,6 +165,7 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -258,6 +263,7 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA=
|
||||
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ=
|
||||
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
@@ -301,6 +307,8 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
|
||||
@@ -309,6 +317,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=
|
||||
@@ -354,10 +364,13 @@ github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcs
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
|
||||
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
|
||||
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
||||
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
|
||||
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
@@ -367,6 +380,7 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
|
||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||
github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
@@ -471,10 +485,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=
|
||||
@@ -553,6 +563,8 @@ github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVo
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-getter v1.4.1 h1:3A2Mh8smGFcf5M+gmcv898mZdrxpseik45IpcyISLsA=
|
||||
github.com/hashicorp/go-getter v1.4.1/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
@@ -624,6 +636,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=
|
||||
@@ -721,6 +735,7 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k=
|
||||
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-shellwords v1.0.6 h1:9Jok5pILi5S1MnDirGVTufYGtksUs/V2BWUP3ZkeUUI=
|
||||
@@ -771,6 +786,7 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||
@@ -828,6 +844,7 @@ github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
@@ -885,14 +902,14 @@ 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.16 h1:R3gCZs4A3EHPNx4B7p1idWD+OhyaU/bAlGYBWc0ZNz4=
|
||||
github.com/qlik-oss/k-apis v0.1.16/go.mod h1:AkNa/kaZHpGVs9l+pHe6nvz99Sp9WO1f9ylBES95o+I=
|
||||
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/qlik-oss/kustomize/api v0.5.2-0.20200820111149-1a59db58525f h1:a6TNAGEyKqlMzDQwwoVwBjcRZ47ZnXxk3ue+zy5I4Qk=
|
||||
github.com/qlik-oss/kustomize/api v0.5.2-0.20200820111149-1a59db58525f/go.mod h1:H7m776WsaBG9jFljChQXd2bGlzTJvQRHDESRQ96psXU=
|
||||
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA=
|
||||
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
|
||||
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=
|
||||
@@ -963,6 +980,8 @@ github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
|
||||
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
@@ -983,6 +1002,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@@ -1037,6 +1057,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofm
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yujunz/go-getter v1.4.1-lite h1:FhvNc94AXMZkfqUwfMKhnQEC9phkphSGdPTL7tIdhOM=
|
||||
github.com/yujunz/go-getter v1.4.1-lite/go.mod h1:sbmqxXjyLunH1PkF3n7zSlnVeMvmYUuIl9ZVs/7NyCc=
|
||||
@@ -1063,6 +1085,9 @@ go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
@@ -1134,6 +1159,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1170,8 +1196,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=
|
||||
@@ -1348,6 +1372,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
@@ -1380,8 +1405,12 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20190924164351-c8b7dadae555 h1:4Yrwvx9yMvZx+vK3wdX7aX2UCNZJJn0TDc+BNOJTE00=
|
||||
gopkg.in/yaml.v3 v3.0.0-20190924164351-c8b7dadae555/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.0.0 h1:d+tVGRu6X0ZBQ+kyAR8JKi6AXhTP2gmQaoIYaGFz634=
|
||||
@@ -1443,9 +1472,13 @@ rsc.io/letsencrypt v0.0.3 h1:H7xDfhkaFFSYEJlKeq38RwX2jYcnTeHuDQyT+mMNMwM=
|
||||
rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1 h1:NEqA/35upoAjb+I5vh1ODUqxoX4DOrezeQa9BhhG5Co=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
site_name: Qlik Sense on Kubernetes CLI
|
||||
repo_url: 'https://github.com/qlik-oss/sense-installer'
|
||||
strict: true
|
||||
|
||||
theme:
|
||||
name: "material"
|
||||
palette:
|
||||
primary: 'green'
|
||||
accent: 'indigo'
|
||||
|
||||
markdown_extensions:
|
||||
- toc:
|
||||
permalink: true
|
||||
@@ -14,6 +16,8 @@ markdown_extensions:
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.superfences
|
||||
- pymdownx.details
|
||||
- pymdownx.tabbed
|
||||
|
||||
nav:
|
||||
- Overview: index.md
|
||||
- getting_started.md
|
||||
|
||||
@@ -144,17 +144,17 @@ func (cr *QliksenseCR) IsRepoExist() bool {
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) GetFetchUrl() string {
|
||||
if cr.Spec.FetchSource == nil || cr.Spec.FetchSource.Repository == "" {
|
||||
if cr.Spec.Git == nil || cr.Spec.Git.Repository == "" {
|
||||
return QLIK_GIT_REPO
|
||||
}
|
||||
return cr.Spec.FetchSource.Repository
|
||||
return cr.Spec.Git.Repository
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) GetFetchAccessToken(encryptionKey string) string {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
if cr.Spec.Git == nil {
|
||||
return ""
|
||||
}
|
||||
if tok, err := cr.Spec.FetchSource.GetAccessToken(); err != nil {
|
||||
if tok, err := cr.Spec.Git.GetAccessToken(); err != nil {
|
||||
fmt.Println(err)
|
||||
return ""
|
||||
} else if tok == "" {
|
||||
@@ -171,29 +171,29 @@ func (cr *QliksenseCR) GetFetchAccessToken(encryptionKey string) string {
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) SetFetchUrl(url string) {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
cr.Spec.FetchSource = &config.Repo{}
|
||||
if cr.Spec.Git == nil {
|
||||
cr.Spec.Git = &config.Repo{}
|
||||
}
|
||||
cr.Spec.FetchSource.Repository = url
|
||||
cr.Spec.Git.Repository = url
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) SetFetchAccessToken(token, encryptionKey string) error {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
cr.Spec.FetchSource = &config.Repo{}
|
||||
if cr.Spec.Git == nil {
|
||||
cr.Spec.Git = &config.Repo{}
|
||||
}
|
||||
res, err := EncryptData([]byte(token), encryptionKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cr.Spec.FetchSource.AccessToken = b64.StdEncoding.EncodeToString(res)
|
||||
cr.Spec.Git.AccessToken = b64.StdEncoding.EncodeToString(res)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) SetFetchAccessSecretName(sec string) {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
cr.Spec.FetchSource = &config.Repo{}
|
||||
if cr.Spec.Git == nil {
|
||||
cr.Spec.Git = &config.Repo{}
|
||||
}
|
||||
cr.Spec.FetchSource.SecretName = sec
|
||||
cr.Spec.Git.SecretName = sec
|
||||
}
|
||||
|
||||
//DeleteRepo delete the manifest repo and unset manifestsRoot
|
||||
@@ -524,9 +524,9 @@ func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error)
|
||||
}
|
||||
newCr.Spec.Secrets = finalSecrets
|
||||
|
||||
if newCr.Spec.FetchSource != nil && newCr.Spec.FetchSource.AccessToken != "" {
|
||||
if newCr.Spec.Git != nil && newCr.Spec.Git.AccessToken != "" {
|
||||
decData := cr.GetFetchAccessToken(encryptionKey)
|
||||
newCr.Spec.FetchSource.AccessToken = decData
|
||||
newCr.Spec.Git.AccessToken = decData
|
||||
}
|
||||
return newCr, nil
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ func TestGetDecryptedCr(t *testing.T) {
|
||||
if decryptedValue == orignalValue {
|
||||
t.Fail()
|
||||
}
|
||||
if newCr.Spec.FetchSource.AccessToken != "mytoken" {
|
||||
if newCr.Spec.Git.AccessToken != "mytoken" {
|
||||
t.Fail()
|
||||
}
|
||||
td()
|
||||
|
||||
@@ -508,7 +508,7 @@ func TestClientGoUtils_CreatePreflightTestPod(t *testing.T) {
|
||||
ImagePullPolicy: apiv1.PullIfNotPresent,
|
||||
Command: []string{"echo"},
|
||||
VolumeMounts: []apiv1.VolumeMount{
|
||||
apiv1.VolumeMount{
|
||||
{
|
||||
Name: "secret1",
|
||||
MountPath: filepath.Dir("/etc/secret1"),
|
||||
ReadOnly: true,
|
||||
@@ -517,7 +517,7 @@ func TestClientGoUtils_CreatePreflightTestPod(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Volumes: []apiv1.Volume{
|
||||
apiv1.Volume{
|
||||
{
|
||||
Name: "secret1",
|
||||
VolumeSource: apiv1.VolumeSource{
|
||||
Secret: &apiv1.SecretVolumeSource{
|
||||
@@ -844,7 +844,7 @@ func TestClientGoUtils_WaitForPod(t *testing.T) {
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
apiv1.Container{},
|
||||
{},
|
||||
},
|
||||
},
|
||||
Status: apiv1.PodStatus{
|
||||
@@ -895,7 +895,7 @@ func TestClientGoUtils_WaitForPod(t *testing.T) {
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
apiv1.Container{},
|
||||
{},
|
||||
},
|
||||
},
|
||||
Status: apiv1.PodStatus{
|
||||
@@ -942,7 +942,7 @@ func TestClientGoUtils_WaitForPod(t *testing.T) {
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
apiv1.Container{},
|
||||
{},
|
||||
},
|
||||
},
|
||||
Status: apiv1.PodStatus{
|
||||
@@ -1059,7 +1059,7 @@ func TestClientGoUtils_WaitForPodToDie(t *testing.T) {
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
apiv1.Container{},
|
||||
{},
|
||||
},
|
||||
},
|
||||
Status: apiv1.PodStatus{
|
||||
@@ -1131,7 +1131,7 @@ func TestClientGoUtils_WaitForPodToDie(t *testing.T) {
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
apiv1.Container{},
|
||||
{},
|
||||
},
|
||||
},
|
||||
Status: apiv1.PodStatus{
|
||||
@@ -1885,10 +1885,10 @@ func TestClientGoUtils_waitForStatefulsetToDelete(t *testing.T) {
|
||||
statefulsetName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
timeoutForChangingReplicaCount time.Duration
|
||||
}{
|
||||
{
|
||||
@@ -1897,8 +1897,8 @@ func TestClientGoUtils_waitForStatefulsetToDelete(t *testing.T) {
|
||||
Verbose: true,
|
||||
},
|
||||
args: args{
|
||||
clientset: fake.NewSimpleClientset(ss),
|
||||
namespace: "test-ns",
|
||||
clientset: fake.NewSimpleClientset(ss),
|
||||
namespace: "test-ns",
|
||||
statefulsetName: ss.Name,
|
||||
},
|
||||
wantErr: false,
|
||||
@@ -1910,11 +1910,11 @@ func TestClientGoUtils_waitForStatefulsetToDelete(t *testing.T) {
|
||||
Verbose: true,
|
||||
},
|
||||
args: args{
|
||||
clientset: fake.NewSimpleClientset(),
|
||||
namespace: "test-ns",
|
||||
clientset: fake.NewSimpleClientset(),
|
||||
namespace: "test-ns",
|
||||
statefulsetName: ss.Name,
|
||||
},
|
||||
wantErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "timeout",
|
||||
@@ -1922,11 +1922,11 @@ func TestClientGoUtils_waitForStatefulsetToDelete(t *testing.T) {
|
||||
Verbose: true,
|
||||
},
|
||||
args: args{
|
||||
clientset: fake.NewSimpleClientset(ss),
|
||||
namespace: "test-ns",
|
||||
clientset: fake.NewSimpleClientset(ss),
|
||||
namespace: "test-ns",
|
||||
statefulsetName: ss.Name,
|
||||
},
|
||||
wantErr: true,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -23,7 +23,6 @@ const (
|
||||
QliksenseKind = "Qliksense"
|
||||
QliksenseGroup = "qlik.com"
|
||||
QliksenseDefaultProfile = "docker-desktop"
|
||||
DefaultRotateKeys = "yes"
|
||||
QliksenseMetadataName = "QliksenseConfigMetadata"
|
||||
DefaultMongodbUri = "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"
|
||||
DefaultMongodbUriKey = "mongodbUri"
|
||||
@@ -38,8 +37,7 @@ func (qliksenseCR *QliksenseCR) AddCommonConfig(contextName string) {
|
||||
})
|
||||
qliksenseCR.SetName(contextName)
|
||||
qliksenseCR.Spec = &config.CRSpec{
|
||||
Profile: QliksenseDefaultProfile,
|
||||
RotateKeys: DefaultRotateKeys,
|
||||
Profile: QliksenseDefaultProfile,
|
||||
}
|
||||
qliksenseCR.Spec.AddToSecrets("qliksense", DefaultMongodbUriKey, strings.Replace(DefaultMongodbUri, "qlik-default", contextName, 1), "")
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ func TestAddCommonConfig(t *testing.T) {
|
||||
q.SetName("myqliksense")
|
||||
q.SetGroupVersionKind(gvk)
|
||||
q.Spec = &config.CRSpec{
|
||||
Profile: QliksenseDefaultProfile,
|
||||
RotateKeys: DefaultRotateKeys,
|
||||
Profile: QliksenseDefaultProfile,
|
||||
Secrets: map[string]config.NameValues{
|
||||
"qliksense": []config.NameValue{{
|
||||
Name: DefaultMongodbUriKey,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -103,6 +103,16 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
|
||||
}
|
||||
totalCount++
|
||||
|
||||
// Preflight verify ca chain check
|
||||
if err := qp.VerifyCAChain(kubeConfigContents, namespace, preflightOpts, false); 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 preflight checks were successful
|
||||
return nil
|
||||
|
||||
@@ -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
|
||||
|
||||
123
pkg/preflight/verify_ca.go
Normal file
123
pkg/preflight/verify_ca.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package preflight
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func (qp *QliksensePreflight) VerifyCAChain(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error {
|
||||
|
||||
var currentCR *qapi.QliksenseCR
|
||||
var err error
|
||||
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
|
||||
qConfig.SetNamespace(namespace)
|
||||
|
||||
fmt.Print("Preflight verify-ca-chain check... ")
|
||||
qp.CG.LogVerboseMessage("\n----------------------------------- \n")
|
||||
|
||||
currentCR, err = qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
qp.CG.LogVerboseMessage("Unable to retrieve current CR: %v\n", err)
|
||||
return err
|
||||
}
|
||||
decryptedCR, err := qConfig.GetDecryptedCr(currentCR)
|
||||
if err != nil {
|
||||
qp.CG.LogVerboseMessage("An error occurred while retrieving mongodbUrl from current CR: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// infer ca certs form CR
|
||||
caCertificates := strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("qliksense", "caCertificates"))
|
||||
|
||||
fmt.Println("Openssl verify mongodbUrl:")
|
||||
// infer mongodb url from CR
|
||||
mongodbUrl := strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("qliksense", "mongodbUri"))
|
||||
qp.CG.LogVerboseMessage("Mongodb url inferred form CR: %s\n", mongodbUrl)
|
||||
|
||||
// parse out server and port from mongodb url and execute openssl verify
|
||||
if err := qp.extractCertAndVerify(mongodbUrl, caCertificates); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("\nOpenssl verify discoveryUrl:\n")
|
||||
// infer idpConfigs form CR
|
||||
idpConfigs := strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("identity-providers", "idpConfigs"))
|
||||
|
||||
data := []map[string]interface{}{}
|
||||
if err := json.Unmarshal([]byte(idpConfigs), &data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var discoveryUrl string
|
||||
for _, idpData := range data {
|
||||
discoveryUrl = idpData["discoveryUrl"].(string)
|
||||
qp.CG.LogVerboseMessage("Discovery url: %s\n", discoveryUrl)
|
||||
}
|
||||
if err := qp.extractCertAndVerify(discoveryUrl, caCertificates); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qp.CG.LogVerboseMessage("Completed preflight verify-ca-chain check\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qp *QliksensePreflight) extractCertAndVerify(server string, caCertificates string) error {
|
||||
u, err := url.Parse(server)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse url: %v", err)
|
||||
}
|
||||
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "http":
|
||||
return fmt.Errorf("http url is not supported for this operation")
|
||||
case "https":
|
||||
if u.Port() == "" {
|
||||
u.Host += ":443"
|
||||
}
|
||||
}
|
||||
|
||||
qp.CG.LogVerboseMessage("Host: %s, port: %s\n", u.Host, u.Port())
|
||||
conn, err := tls.Dial("tcp", u.Host, &tls.Config{})
|
||||
qp.CG.LogVerboseMessage("Host: %s\n", u.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: " + err.Error())
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Get the ConnectionState struct as that's the one which gives us x509.Certificate struct
|
||||
x509Certificates := conn.ConnectionState().PeerCertificates
|
||||
|
||||
var serverCert *x509.Certificate
|
||||
if len(x509Certificates) == 0 {
|
||||
return fmt.Errorf("no server certificates retrieved from the server")
|
||||
}
|
||||
// we retrieve and verify the server certificate, we ignore intermediate certificates at this point.
|
||||
for _, x509Cert := range x509Certificates {
|
||||
if !x509Cert.IsCA {
|
||||
serverCert = x509Cert
|
||||
break
|
||||
}
|
||||
}
|
||||
if serverCert == nil {
|
||||
return fmt.Errorf("no valid server certificates retrieved from the server")
|
||||
}
|
||||
roots := x509.NewCertPool()
|
||||
if ok := roots.AppendCertsFromPEM([]byte(caCertificates)); !ok {
|
||||
return fmt.Errorf("failed to parse root certificate.")
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: roots,
|
||||
DNSName: u.Hostname(),
|
||||
}
|
||||
if _, err := serverCert.Verify(opts); err != nil {
|
||||
return fmt.Errorf("failed to verify certificate: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -5,16 +5,19 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qlik-oss/k-apis/pkg/config"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/qlik-oss/k-apis/pkg/cr"
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"k8s.io/kubectl/pkg/cmd/util/editor"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -73,20 +76,21 @@ func (q *Qliksense) configEjson() error {
|
||||
}
|
||||
|
||||
func (q *Qliksense) applyConfigToK8s(qcr *qapi.QliksenseCR) error {
|
||||
if qcr.Spec.RotateKeys != "None" {
|
||||
if err := q.configEjson(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := q.configEjson(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userHomeDir, err := homedir.Dir()
|
||||
if err != nil {
|
||||
fmt.Printf(`error fetching user's home directory: %v\n`, err)
|
||||
return err
|
||||
}
|
||||
fmt.Println("Manifests root: " + qcr.Spec.GetManifestsRoot())
|
||||
qcr.SetNamespace(qapi.GetKubectlNamespace())
|
||||
b, _ := yaml.Marshal(qcr.KApiCr)
|
||||
fmt.Printf("%v", string(b))
|
||||
// os.Exit(0)
|
||||
// generate patches
|
||||
cr.GeneratePatches(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config"))
|
||||
cr.GeneratePatches(&qcr.KApiCr, config.KeysActionRestoreOrRotate, path.Join(userHomeDir, ".kube", "config"))
|
||||
// apply generated manifests
|
||||
profilePath := filepath.Join(qcr.Spec.GetManifestsRoot(), qcr.Spec.GetProfileDir())
|
||||
fmt.Printf("Generating manifests for profile: %v\n", profilePath)
|
||||
@@ -192,13 +196,12 @@ func (q *Qliksense) EditCR(contextName string) error {
|
||||
if err := ioutil.WriteFile(tempFile.Name(), crContent, os.ModePerm); err != nil {
|
||||
return nil
|
||||
}
|
||||
cmd := exec.Command(getKubeEditorTool(), tempFile.Name())
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
|
||||
currentEditor := editor.NewDefaultEditor([]string{"KUBE_EDITOR", "EDITOR"})
|
||||
if err = currentEditor.Launch(tempFile.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newCr, err := qapi.GetCRObject(tempFile.Name())
|
||||
if err != nil {
|
||||
return errors.New("cannot save the cr. Someting wrong in the file format. It is not saved\n" + err.Error())
|
||||
@@ -213,11 +216,3 @@ func (q *Qliksense) EditCR(contextName string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getKubeEditorTool() string {
|
||||
editor := os.Getenv("KUBE_EDITOR")
|
||||
if editor == "" {
|
||||
editor = "vim"
|
||||
}
|
||||
return editor
|
||||
}
|
||||
|
||||
@@ -176,53 +176,6 @@ func caseInsenstiveFieldByName(v reflect.Value, name string) reflect.Value {
|
||||
return v.FieldByNameFunc(func(n string) bool { return strings.ToLower(n) == name })
|
||||
}
|
||||
|
||||
func validateCR(key string, keySub string, value string, crSpec *api.QliksenseCR) (bool, *api.QliksenseCR) {
|
||||
cr := reflect.ValueOf(crSpec.Spec)
|
||||
keyValid := caseInsenstiveFieldByName(reflect.Indirect(cr), key)
|
||||
if !keyValid.IsValid() {
|
||||
//not in main spec
|
||||
fmt.Println(key, "is an invalid key")
|
||||
return false, crSpec
|
||||
} else if keySub == "" {
|
||||
if key == "rotatekeys" {
|
||||
if _, err := validateInput(value); err != nil {
|
||||
return false, crSpec
|
||||
}
|
||||
}
|
||||
}
|
||||
// checks if it is git or gitops
|
||||
if keySub != "" {
|
||||
if !keyValid.IsNil() {
|
||||
if !caseInsenstiveFieldByName(reflect.Indirect(keyValid), keySub).IsValid() {
|
||||
fmt.Println(keySub, "is an invalid key")
|
||||
return false, crSpec
|
||||
} else {
|
||||
// verify gitops enabled and gitops schedule
|
||||
switch keySub {
|
||||
case "schedule":
|
||||
if _, err := cron.ParseStandard(value); err != nil {
|
||||
fmt.Println("Please enter string with standard cron scheduling syntax ")
|
||||
return false, crSpec
|
||||
}
|
||||
case "enabled":
|
||||
if !strings.EqualFold(value, "yes") && !strings.EqualFold(value, "no") {
|
||||
fmt.Println("Please use yes or no for key enabled")
|
||||
return false, crSpec
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch key {
|
||||
case "opsrunner":
|
||||
crSpec.Spec.OpsRunner = &config.OpsRunner{}
|
||||
case "git":
|
||||
crSpec.Spec.Git = &config.Repo{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, crSpec
|
||||
}
|
||||
|
||||
// SetOtherConfigs - set profile/storageclassname/git.repository/manifestRoot commands
|
||||
func (q *Qliksense) SetOtherConfigs(args []string) error {
|
||||
// retieve current context from config.yaml
|
||||
@@ -240,11 +193,7 @@ func (q *Qliksense) SetOtherConfigs(args []string) error {
|
||||
}
|
||||
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "fetchSource.") {
|
||||
if err := q.processSetFetchSource(arg, qliksenseCR); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if strings.HasPrefix(arg, "git.") {
|
||||
if strings.HasPrefix(arg, "git.") {
|
||||
if err := q.processSetGit(arg, qliksenseCR); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -273,64 +222,34 @@ func processSetSingleArg(arg string, cr *api.QliksenseCR) error {
|
||||
cr.Spec.Profile = nv[1]
|
||||
case "storageClassName":
|
||||
cr.Spec.StorageClassName = nv[1]
|
||||
case "rotateKeys":
|
||||
valid := false
|
||||
for _, v := range []string{"yes", "no", "None"} {
|
||||
if nv[1] == v {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return errors.New("please povide rotateKeys=yes|no|None")
|
||||
}
|
||||
cr.Spec.RotateKeys = nv[1]
|
||||
default:
|
||||
return errors.New("Please enter one of: profile, storageClassName,rotateKeys, manifestRoot to configure the current context")
|
||||
return errors.New("Please enter one of: profile, storageClassName, manifestRoot, git to configure the current context")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) processSetFetchSource(arg string, cr *api.QliksenseCR) error {
|
||||
args := strings.Split(arg, "=")
|
||||
subs := strings.Split(args[0], ".")
|
||||
if cr.Spec.FetchSource == nil {
|
||||
cr.Spec.FetchSource = &config.Repo{}
|
||||
func (q *Qliksense) processSetGit(arg string, cr *api.QliksenseCR) error {
|
||||
s := strings.Split(arg, "=")
|
||||
tArg0 := strings.TrimSpace(s[0])
|
||||
tArg1 := strings.TrimSpace(s[1])
|
||||
subs := strings.Split(tArg0, ".")
|
||||
if cr.Spec.Git == nil {
|
||||
cr.Spec.Git = &config.Repo{}
|
||||
}
|
||||
switch subs[1] {
|
||||
case "repository":
|
||||
cr.Spec.FetchSource.Repository = args[1]
|
||||
cr.Spec.Git.Repository = tArg1
|
||||
case "accessToken":
|
||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||
key, err := qConfig.GetEncryptionKeyFor(cr.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cr.SetFetchAccessToken(args[1], key)
|
||||
return cr.SetFetchAccessToken(tArg1, key)
|
||||
case "secretName":
|
||||
cr.Spec.FetchSource.SecretName = args[1]
|
||||
cr.Spec.Git.SecretName = tArg1
|
||||
case "userName":
|
||||
cr.Spec.FetchSource.UserName = args[1]
|
||||
default:
|
||||
return errors.New(arg + " does not match any cr spec")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) processSetGit(arg string, cr *api.QliksenseCR) error {
|
||||
args := strings.Split(arg, "=")
|
||||
subs := strings.Split(args[0], ".")
|
||||
if cr.Spec.Git == nil {
|
||||
cr.Spec.Git = &config.Repo{}
|
||||
}
|
||||
switch subs[1] {
|
||||
case "repository":
|
||||
cr.Spec.Git.Repository = args[1]
|
||||
case "accessToken":
|
||||
cr.Spec.Git.AccessToken = args[1]
|
||||
case "secretName":
|
||||
cr.Spec.Git.SecretName = args[1]
|
||||
case "userName":
|
||||
cr.Spec.Git.UserName = args[1]
|
||||
cr.Spec.Git.UserName = tArg1
|
||||
default:
|
||||
return errors.New(arg + " does not match any cr spec")
|
||||
}
|
||||
|
||||
@@ -96,7 +96,6 @@ metadata:
|
||||
name: qlik-default
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik-default
|
||||
`
|
||||
qlikDefaultContext := "qlik-default"
|
||||
@@ -244,7 +243,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
||||
q: &Qliksense{
|
||||
QliksenseHome: testDir,
|
||||
},
|
||||
args: []string{"profile=minikube", "rotateKeys=yes", "storageClassName=efs", "opsRunner.enabled=yes", "opsRunner.schedule=30 * * * *", "git.repository=master", "git.userName=foo", "git.accessToken=1234"},
|
||||
args: []string{"profile=minikube", "storageClassName=efs", "opsRunner.enabled=yes", "opsRunner.schedule=30 * * * *", "git.repository=master", "git.userName=foo", "git.accessToken=1234"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -254,7 +253,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
||||
q: &Qliksense{
|
||||
QliksenseHome: testDir,
|
||||
},
|
||||
args: []string{"someconfig=somevalue, opsRunner.schedule=bar", "opsRunner.enabled=bar", "git.foo=bar", "rotateKeys=bar"},
|
||||
args: []string{"someconfig=somevalue, opsRunner.schedule=bar", "opsRunner.enabled=bar", "git.foo=bar"},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
@@ -744,7 +743,6 @@ metadata:
|
||||
name: qlik-default
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik-default
|
||||
`
|
||||
qlikDefaultContext := "qlik-default"
|
||||
@@ -763,7 +761,6 @@ metadata:
|
||||
name: qlik1
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik1`
|
||||
|
||||
contextYaml2 :=
|
||||
@@ -774,7 +771,6 @@ metadata:
|
||||
name: qlik2
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik2`
|
||||
|
||||
contextsDir := filepath.Join(testDir, contexts, "qlik1")
|
||||
|
||||
@@ -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{"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()
|
||||
@@ -27,10 +29,6 @@ func TestUnsetAll(t *testing.T) {
|
||||
t.Log("error while getting current cr", err)
|
||||
t.FailNow()
|
||||
}
|
||||
if qcr.Spec.RotateKeys != "" {
|
||||
t.Log("Expected empty rotateKeys but got: " + qcr.Spec.RotateKeys)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if qcr.Spec.Configs["qliksense"] != nil {
|
||||
t.Log("qliksense in configs not deleted")
|
||||
@@ -44,6 +42,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) {
|
||||
@@ -72,7 +78,9 @@ metadata:
|
||||
name: qlik-default
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
opsRunner:
|
||||
enabled: "yes"
|
||||
watchBranch: something
|
||||
configs:
|
||||
qliksense:
|
||||
- name: acceptEula
|
||||
|
||||
@@ -93,11 +93,15 @@ func getQliksenseInitCrds(qcr *qapi.QliksenseCR) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
qInitMsPath := filepath.Join(repoPath, Q_INIT_CRD_PATH)
|
||||
qInitMsPath := filepath.Join(repoPath, "manifests", qcr.Spec.Profile, "crds")
|
||||
if _, err := os.Lstat(qInitMsPath); err != nil {
|
||||
// older version of qliksense-init used
|
||||
qInitMsPath = filepath.Join(repoPath, "manifests/base/manifests/qliksense-init")
|
||||
qInitMsPath = filepath.Join(repoPath, Q_INIT_CRD_PATH)
|
||||
if _, err := os.Lstat(qInitMsPath); err != nil {
|
||||
// older version of qliksense-init used
|
||||
qInitMsPath = filepath.Join(repoPath, "manifests/base/manifests/qliksense-init")
|
||||
}
|
||||
}
|
||||
|
||||
qInitByte, err := ExecuteKustomizeBuild(qInitMsPath)
|
||||
if err != nil {
|
||||
fmt.Println("cannot generate crds for qliksense-init", err)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -268,7 +268,6 @@ spec:
|
||||
- name: imageRegistry
|
||||
value: %s
|
||||
manifestsRoot: %s
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik-default
|
||||
`, version, registry.url, manifestsRootDir)
|
||||
setupQliksenseTestDefaultContext(t, tmpQlikSenseHome, cr)
|
||||
|
||||
@@ -94,7 +94,6 @@ func fetchAndUpdateCR(qConfig *qapi.QliksenseConfig, version string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destDir := qConfig.BuildRepoPath(version)
|
||||
fmt.Printf("fetching version [%s] from %s\n", version, qcr.GetFetchUrl())
|
||||
if err := qapi.CopyDirectory(tempDest, destDir); err != nil {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/qlik-oss/k-apis/pkg/git"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
@@ -22,8 +24,20 @@ func (q *Qliksense) GetInstallableVersions(opts *LsRemoteCmdOptions) error {
|
||||
}
|
||||
|
||||
var repoPath string
|
||||
var auth transport.AuthMethod
|
||||
if qcr.Spec.GetManifestsRoot() != "" {
|
||||
repoPath = qcr.Spec.GetManifestsRoot()
|
||||
encKey, err := qConfig.GetEncryptionKeyFor(qcr.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessToken := qcr.GetFetchAccessToken(encKey)
|
||||
if accessToken != "" {
|
||||
auth = &http.BasicAuth{
|
||||
Username: "something",
|
||||
Password: accessToken,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repoPath, err = DownloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
|
||||
if err != nil {
|
||||
@@ -36,7 +50,7 @@ func (q *Qliksense) GetInstallableVersions(opts *LsRemoteCmdOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
remoteRefsList, err := git.GetRemoteRefs(r, nil,
|
||||
remoteRefsList, err := git.GetRemoteRefs(r, auth,
|
||||
&git.RemoteRefConstraints{
|
||||
Include: true,
|
||||
Sort: true,
|
||||
@@ -96,13 +110,18 @@ func getLatestTag(repoUrl, accessToken string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
r, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
remoteRefsList, err := git.GetRemoteRefs(r, nil,
|
||||
var auth transport.AuthMethod
|
||||
if accessToken != "" {
|
||||
auth = &http.BasicAuth{
|
||||
Username: "something",
|
||||
Password: accessToken,
|
||||
}
|
||||
}
|
||||
remoteRefsList, err := git.GetRemoteRefs(r, auth,
|
||||
&git.RemoteRefConstraints{
|
||||
Include: true,
|
||||
Sort: true,
|
||||
|
||||
@@ -7,6 +7,11 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mattn/go-tty"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/qlik-oss/k-apis/pkg/config"
|
||||
@@ -17,53 +22,71 @@ import (
|
||||
)
|
||||
|
||||
type InstallCommandOptions struct {
|
||||
StorageClass string
|
||||
MongodbUri string
|
||||
RotateKeys string
|
||||
DryRun bool
|
||||
StorageClass string
|
||||
MongodbUri string
|
||||
AcceptEULA string
|
||||
DryRun bool
|
||||
Pull bool
|
||||
Push bool
|
||||
CleanPatchFiles bool
|
||||
RotateKeys 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 !qcr.IsRepoExist() {
|
||||
if err := fetchAndUpdateCR(qConfig, version); err != nil {
|
||||
return err
|
||||
} else if qcr, err = qConfig.GetCurrentCR(); err != nil {
|
||||
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, "")
|
||||
}
|
||||
if opts.StorageClass != "" {
|
||||
qcr.Spec.StorageClassName = opts.StorageClass
|
||||
}
|
||||
if opts.RotateKeys != "" {
|
||||
qcr.Spec.RotateKeys = opts.RotateKeys
|
||||
|
||||
if err := qConfig.WriteCurrentContextCR(qcr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
qcr.Spec.RotateKeys = "None"
|
||||
userHomeDir, _ := homedir.Dir()
|
||||
fmt.Println("Generating patches only")
|
||||
cr.GeneratePatches(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config"))
|
||||
cr.GeneratePatches(&qcr.KApiCr, config.KeysActionDoNothing, path.Join(userHomeDir, ".kube", "config"))
|
||||
return nil
|
||||
}
|
||||
qConfig.WriteCurrentContextCR(qcr)
|
||||
|
||||
if installed, err := q.CheckAllCrdsInstalled(); err != nil {
|
||||
fmt.Println("error verifying whether CRDs are installed", err)
|
||||
@@ -72,6 +95,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
|
||||
}
|
||||
@@ -93,6 +132,18 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, cle
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.RotateKeys {
|
||||
fmt.Println("Deleting stored application keys")
|
||||
if err := q.DeleteKeysClusterBackup(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
qcr.AddLabelToCr("keys-rotated", strconv.FormatInt(time.Now().Unix(), 10))
|
||||
if err := qConfig.WriteCurrentContextCR(qcr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if qcr.Spec.OpsRunner != nil {
|
||||
// fetching and applying manifest will be in the operator controller
|
||||
// get decrypted cr
|
||||
@@ -102,35 +153,16 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, cle
|
||||
return q.applyCR(dcr)
|
||||
}
|
||||
}
|
||||
if !qcr.IsRepoExist() {
|
||||
if err := fetchAndUpdateCR(qConfig, version); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
qcr, err = qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
fmt.Println("cannot get the current-context cr", err)
|
||||
return err
|
||||
} else if qcr.Spec.GetManifestsRoot() == "" {
|
||||
return errors.New("cannot get the manifest root. Use qliksense fetch <version> or qliksense set manifestsRoot")
|
||||
}
|
||||
|
||||
// install generated manifests into cluster
|
||||
fmt.Println("Installing generated manifests into the cluster")
|
||||
|
||||
if dcr, err := qConfig.GetDecryptedCr(qcr); err != nil {
|
||||
return err
|
||||
} else if err := q.applyConfigToK8s(dcr); err != nil {
|
||||
fmt.Println("cannot do kubectl apply on manifests")
|
||||
return err
|
||||
} else {
|
||||
if IsQliksenseInstalled(dcr.GetName()) {
|
||||
return q.UpgradeQK8s(cleanPatchFiles)
|
||||
}
|
||||
if err := q.applyConfigToK8s(dcr); err != nil {
|
||||
fmt.Println("cannot do kubectl apply on manifests")
|
||||
return err
|
||||
} else {
|
||||
return q.applyCR(dcr)
|
||||
}
|
||||
return q.applyCR(dcr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,3 +258,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,17 +5,15 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func TestCreateK8sResourceBeforePatch(t *testing.T) {
|
||||
@@ -45,18 +43,10 @@ spec:
|
||||
qliksense:
|
||||
- name: mongodbUri
|
||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"`
|
||||
profile: docker-desktop`
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
21
pkg/qliksense/keys.go
Normal file
21
pkg/qliksense/keys.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/qlik-oss/k-apis/pkg/cr"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func (q *Qliksense) DeleteKeysClusterBackup() error {
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
if qcr, err := qConfig.GetCurrentCR(); err != nil {
|
||||
return err
|
||||
} else if userHomeDir, err := homedir.Dir(); err != nil {
|
||||
return err
|
||||
} else if err := cr.DeleteKeysClusterBackup(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -276,9 +276,11 @@ func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
|
||||
configPath := filepath.Join(tmpDir, "config")
|
||||
if repo, err := kapis_git.CloneRepository(configPath, defaultConfigRepoGitUrl, nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v\n", err)
|
||||
} else if err := kapis_git.Checkout(repo, "e38df644e759abf0b5941c1511d1a2cd5e3c42fa", "", nil); err != nil {
|
||||
} else if err := kapis_git.Checkout(repo, "e38df644e759abf0b5941c1511d1a2cd5e3c42fa", "commit-e38df644e759abf0b5941c1511d1a2cd5e3c42fa", nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v\n", err)
|
||||
}
|
||||
//tmpDir := "/var/folders/mf/5hs1qkq508q_scjbhxhmf9qwjrp346/T/679268230"
|
||||
//configPath := "/var/folders/mf/5hs1qkq508q_scjbhxhmf9qwjrp346/T/679268230/config"
|
||||
|
||||
cr := &config.CRSpec{
|
||||
ManifestsRoot: configPath,
|
||||
@@ -310,8 +312,8 @@ func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
|
||||
}
|
||||
break
|
||||
}
|
||||
if resource["kind"].(string) == "Secret" && strings.Contains(resource["metadata"].(map[interface {}]interface {})["name"].(string), "users-secrets-") {
|
||||
keyIdBase64 = resource["data"].(map[interface {}]interface {})["tokenAuthPrivateKeyId"].(string)
|
||||
if resource["kind"].(string) == "Secret" && strings.Contains(resource["metadata"].(map[interface{}]interface{})["name"].(string), "users-secrets-") {
|
||||
keyIdBase64 = resource["data"].(map[interface{}]interface{})["tokenAuthPrivateKeyId"].(string)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -69,8 +55,8 @@ func (q *Qliksense) loadCrStringIntoFileSystem(crstr string, overwriteExistingCo
|
||||
}
|
||||
}
|
||||
}
|
||||
if cr.Spec.FetchSource != nil && cr.Spec.FetchSource.AccessToken != "" {
|
||||
if err := cr.SetFetchAccessToken(cr.Spec.FetchSource.AccessToken, encryptionKey); err != nil {
|
||||
if cr.Spec.Git != nil && cr.Spec.Git.AccessToken != "" {
|
||||
if err := cr.SetFetchAccessToken(cr.Spec.Git.AccessToken, encryptionKey); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
@@ -37,8 +34,7 @@ spec:
|
||||
qliksense:
|
||||
- name: mongodbUri
|
||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"`
|
||||
profile: docker-desktop`
|
||||
sampleCr2 := `
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
@@ -64,8 +60,7 @@ spec:
|
||||
qliksense:
|
||||
- name: mongodbUri
|
||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"`
|
||||
profile: docker-desktop`
|
||||
|
||||
duplicateCr := `
|
||||
apiVersion: qlik.com/v1
|
||||
@@ -79,30 +74,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 +106,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()
|
||||
|
||||
@@ -25,7 +25,6 @@ func (q *Qliksense) UpgradeQK8s(cleanPatchFiles bool) error {
|
||||
fmt.Println("cannot get the current-context cr", err)
|
||||
return err
|
||||
}
|
||||
qcr.Spec.RotateKeys = "no"
|
||||
|
||||
dcr, err := qConfig.GetDecryptedCr(qcr)
|
||||
if err != nil {
|
||||
|
||||
23
update_kustomize.md
Normal file
23
update_kustomize.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Update Kustomize dependency
|
||||
|
||||
To update the kustomize dependency in `go.mod` file, run the following command for the vesion `v0.0.43`
|
||||
|
||||
```console
|
||||
GOPROXY=direct go get github.com/qlik-oss/kustomize/api@qlik/v0.0.43
|
||||
```
|
||||
|
||||
The above command will generate output something like this
|
||||
|
||||
```console
|
||||
go: downloading github.com/qlik-oss/kustomize v3.3.2-0.20200820111149-1a59db58525f+incompatible
|
||||
go: github.com/qlik-oss/kustomize/api qlik/v0.0.43 => v0.5.2-0.20200820111149-1a59db58525f
|
||||
go get: github.com/qlik-oss/kustomize/api@v0.5.2-0.20200820111149-1a59db58525f: parsing go.mod:
|
||||
module declares its path as: sigs.k8s.io/kustomize/api
|
||||
but was required as: github.com/qlik-oss/kustomize/api
|
||||
```
|
||||
|
||||
So in the `go.mod` file write
|
||||
|
||||
```console
|
||||
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.5.2-0.20200820111149-1a59db58525f
|
||||
```
|
||||
Reference in New Issue
Block a user