Compare commits

...

21 Commits

Author SHA1 Message Date
Andriy Bulynko
fc62e5fea4 Upgrading kustomize API to version qlik/v0.0.28 (#408) 2020-06-11 23:18:31 -04:00
Andriy Bulynko
bf4e4b9bf1 Require EULA acceptance for install only (#405) 2020-06-11 10:35:41 -04:00
Ashwathi Shiva
4cb3231a08 Preflight images pinned to a specific tag (#404)
* Pinned netcat, nginx and preflight-mongo images to specific tags
2020-06-11 09:32:53 -04:00
Ashwathi Shiva
e1dbcfaac8 added a better error message to a specific scenario in preflight role/rolebinding/serviceaccount check (#403) 2020-06-09 23:32:05 -04:00
Foysal Iqbal
77a3bf4581 Merge pull request #401 from qlik-oss/upgrade-kapi
upgrade k-api and remove config-apply
2020-06-09 22:45:52 -04:00
Foysal Iqbal
a670b6c750 update to dev kapi v0.1.7
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-09 18:17:36 -04:00
Foysal Iqbal
492e4a1baa upgrade k-api and remove config-apply
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-09 17:21:09 -04:00
Foysal Iqbal
3b54a7f0b2 Merge pull request #400 from qlik-oss/fix-default-mongo
fix default mongo
2020-06-09 16:20:40 -04:00
Foysal Iqbal
55cfc42257 fix default mongo
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-09 15:41:52 -04:00
Foysal Iqbal
12e2bec618 fix default mongo
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-09 15:23:59 -04:00
Ashwathi Shiva
2ed59321e4 Preflight checks unit tests and bug fixes (#399)
* - committing changes lost during conflict resolution
- adding more tests
- marking tests to run in parallel to speed things up
- changed case of mongoDbUrl to mongodbUrl
- modified preflight -all output
2020-06-09 13:00:35 -04:00
Andriy Bulynko
98198a3c8b Renaming "keep-config-repo-patches" flag to "clean" and fixing a typo (#396) 2020-06-05 01:01:35 -04:00
Boris Kuschel
afab3e2939 Fix qliksense about (#397) 2020-06-04 17:19:23 -04:00
Andriy Bulynko
62fda8f2c6 Upgrading kustomize API to version qlik/v0.0.27 (#398) 2020-06-04 16:33:50 -04:00
Andriy Bulynko
6af87ab00a Upgrading kustomize API to version qlik/v0.0.26 (#395) 2020-06-04 10:41:31 -04:00
Foysal Iqbal
0b60838b52 add [] for special case (#392)
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-04 09:48:10 -04:00
Foysal Iqbal
bb2974fe66 Merge branch 'master' into fix-regex 2020-06-04 09:20:19 -04:00
Andriy Bulynko
97b2239c2e crds view/install commands have option --all set to true by default (#391) 2020-06-04 04:00:23 -04:00
Foysal Iqbal
9eff54d9ec remove git functionality (#394)
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-04 01:37:25 -04:00
Foysal Iqbal
2d0a2a32bf Merge branch 'master' into fix-regex 2020-06-03 16:19:48 -04:00
Foysal Iqbal
d7238e2b3c add [] for special case
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-03 16:18:51 -04:00
40 changed files with 2029 additions and 797 deletions

View File

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

View File

@@ -12,31 +12,35 @@ var crdsCmd = &cobra.Command{
}
func crdsViewCmd(q *qliksense.Qliksense) *cobra.Command {
opts := &qliksense.CrdCommandOptions{}
opts := &qliksense.CrdCommandOptions{
All: true,
}
c := &cobra.Command{
Use: "view",
Short: "View CRDs for qliksense application. use view --all to see opearator crd as well ",
Long: `View CRDs for qliksense application. use view --all to see opearator crd as well`,
Short: "View CRDs for qliksense application. Use view --all=false to exclude the operator CRD",
Long: "View CRDs for qliksense application. Use view --all=false to exclude the operator CRD",
RunE: func(cmd *cobra.Command, args []string) error {
return q.ViewCrds(opts)
},
}
f := c.Flags()
f.BoolVarP(&opts.All, "all", "", false, "Include All CRDs")
f.BoolVarP(&opts.All, "all", "", opts.All, "If set to false, then the operator CRD is excluded")
return c
}
func crdsInstallCmd(q *qliksense.Qliksense) *cobra.Command {
opts := &qliksense.CrdCommandOptions{}
opts := &qliksense.CrdCommandOptions{
All: true,
}
c := &cobra.Command{
Use: "install",
Short: "Install CRDs fro Qliksense application. Use install --all to include operator crd",
Long: `Install CRDs fro Qliksense application. Use install --all to include 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)
},
}
f := c.Flags()
f.BoolVarP(&opts.All, "all", "", false, "Include All CRDs")
f.BoolVarP(&opts.All, "all", "", opts.All, "If set to false, then the operator CRD is excluded")
return c
}

View File

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

View File

@@ -1,19 +1,15 @@
package main
import (
"bytes"
"fmt"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
)
func installCmd(q *qliksense.Qliksense) *cobra.Command {
opts := &qliksense.InstallCommandOptions{}
opts := &qliksense.InstallCommandOptions{
CleanPatchFiles: true,
}
filePath := ""
keepPatchFiles, pull, push := false, false, false
c := &cobra.Command{
Use: "install",
Short: "install a qliksense release",
@@ -22,76 +18,29 @@ 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, keepPatchFiles, true, pull, push)
}
})
return apply(q, cmd, opts)
} else {
version := ""
if len(args) != 0 {
version = args[0]
}
if err := validatePullPushFlagsOnInstall(q, pull, push); err != nil {
return err
}
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
}
}
return q.InstallQK8s(version, opts, keepPatchFiles)
return q.InstallQK8s(version, opts)
}
},
}
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(&keepPatchFiles, keepPatchFilesFlagName, keepPatchFiles, keepPatchFilesFlagUsage)
f.StringVarP(&filePath, "file", "f", "", "Install from a CR file")
f.BoolVar(&opts.CleanPatchFiles, cleanPatchFilesFlagName, opts.CleanPatchFiles, cleanPatchFilesFlagUsage)
f.BoolVarP(&opts.Pull, pullFlagName, pullFlagShorthand, opts.Pull, pullFlagUsage)
f.BoolVarP(&opts.Push, pushFlagName, pushFlagShorthand, opts.Push, pushFlagUsage)
f.StringVarP(&opts.AcceptEULA, "acceptEULA", "a", opts.AcceptEULA, "Accept EULA for qliksense")
f.BoolVarP(&opts.DryRun, "dry-run", "", false, "Dry run will generate the patches without rotating keys")
f.BoolVarP(&pull, pullFlagName, pullFlagShorthand, pull, pullFlagUsage)
f.BoolVarP(&push, pushFlagName, pushFlagShorthand, push, pushFlagUsage)
return c
}
func validatePullPushFlagsOnInstall(q *qliksense.Qliksense, pull, push bool) error {
if pull && !push {
fmt.Printf("WARNING: pulling images without pushing them")
}
if push {
if err := ensureImageRegistrySetInCR(q); err != nil {
return err
}
}
return nil
}

View File

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

View File

@@ -43,7 +43,7 @@ func pfDnsCheckCmd(q *qliksense.Qliksense) *cobra.Command {
// Preflight DNS check
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight DNS check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
@@ -51,11 +51,11 @@ func pfDnsCheckCmd(q *qliksense.Qliksense) *cobra.Command {
namespace = "default"
}
if err = qp.CheckDns(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight DNS check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
fmt.Fprintf(out, "%s\n", Green("Preflight DNS check PASSED"))
fmt.Fprintf(out, "%s\n", Green("PASSED"))
return nil
},
}
@@ -81,16 +81,16 @@ func pfK8sVersionCheckCmd(q *qliksense.Qliksense) *cobra.Command {
// Preflight Kubernetes minimum version check
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight kubernetes minimum version check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
if err = qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight kubernetes minimum version check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
fmt.Fprintf(out, "%s\n", Green("Preflight kubernetes minimum version check PASSED"))
fmt.Fprintf(out, "%s\n", Green("PASSED"))
return nil
},
}
@@ -157,7 +157,7 @@ func pfDeploymentCheckCmd(q *qliksense.Qliksense) *cobra.Command {
// Preflight deployments check
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
@@ -165,11 +165,11 @@ func pfDeploymentCheckCmd(q *qliksense.Qliksense) *cobra.Command {
namespace = "default"
}
if err = qp.CheckDeployment(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
fmt.Fprintf(out, "%s\n", Green("Preflight deployment check PASSED"))
fmt.Fprintf(out, "%s\n", Green("PASSED"))
return nil
},
}
@@ -195,7 +195,7 @@ func pfServiceCheckCmd(q *qliksense.Qliksense) *cobra.Command {
// Preflight service check
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
@@ -204,11 +204,11 @@ func pfServiceCheckCmd(q *qliksense.Qliksense) *cobra.Command {
namespace = "default"
}
if err = qp.CheckService(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
fmt.Fprintf(out, "%s\n", Green("Preflight service check PASSED"))
fmt.Fprintf(out, "%s\n", Green("PASSED"))
return nil
},
}
@@ -234,7 +234,7 @@ func pfPodCheckCmd(q *qliksense.Qliksense) *cobra.Command {
// Preflight pod check
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
@@ -242,11 +242,11 @@ func pfPodCheckCmd(q *qliksense.Qliksense) *cobra.Command {
namespace = "default"
}
if err = qp.CheckPod(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
fmt.Fprintf(out, "%s\n", Green("Preflight pod check PASSED"))
fmt.Fprintf(out, "%s\n", Green("PASSED"))
return nil
},
}
@@ -272,16 +272,16 @@ func pfCreateRoleCheckCmd(q *qliksense.Qliksense) *cobra.Command {
// Preflight role check
namespace, _, err := qp.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
if err = qp.CheckCreateRole(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
fmt.Fprintf(out, "%s\n", Green("Preflight role check PASSED"))
fmt.Fprintf(out, "%s\n", Green("PASSED"))
return nil
},
}
@@ -307,16 +307,16 @@ func pfCreateRoleBindingCheckCmd(q *qliksense.Qliksense) *cobra.Command {
// Preflight createRoleBinding check
namespace, _, err := qp.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight rolebinding check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
if err = qp.CheckCreateRoleBinding(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight rolebinding check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
fmt.Fprintf(out, "%s\n", Green("Preflight rolebinding check PASSED"))
fmt.Fprintf(out, "%s\n", Green("PASSED"))
return nil
},
}
@@ -342,16 +342,16 @@ func pfCreateServiceAccountCheckCmd(q *qliksense.Qliksense) *cobra.Command {
// Preflight createServiceAccount check
namespace, _, err := qp.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight ServiceAccount check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
if err = qp.CheckCreateServiceAccount(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight ServiceAccount check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
fmt.Fprintf(out, "%s\n", Green("Preflight ServiceAccount check PASSED"))
fmt.Fprintf(out, "%s\n", Green("PASSED"))
return nil
},
}
@@ -376,16 +376,16 @@ func pfCreateAuthCheckCmd(q *qliksense.Qliksense) *cobra.Command {
// Preflight authcheck
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight authcheck FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
if err = qp.CheckCreateRB(namespace, kubeConfigContents); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight authcheck FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
fmt.Fprintf(out, "%s\n", Green("Preflight authcheck PASSED"))
fmt.Fprintf(out, "%s\n", Green("PASSED"))
return nil
},
}
@@ -411,7 +411,7 @@ func pfMongoCheckCmd(q *qliksense.Qliksense) *cobra.Command {
// Preflight mongo check
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight mongo check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
@@ -419,11 +419,11 @@ func pfMongoCheckCmd(q *qliksense.Qliksense) *cobra.Command {
namespace = "default"
}
if err = qp.CheckMongo(kubeConfigContents, namespace, preflightOpts, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight mongo check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n", err)
return nil
}
fmt.Fprintf(out, "%s\n", Green("Preflight mongo check PASSED"))
fmt.Fprintf(out, "%s\n", Green("PASSED"))
return nil
},
}

View File

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

View File

@@ -2,7 +2,6 @@ package main
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
@@ -23,17 +22,17 @@ import (
// qliksense <command>
const (
qlikSenseHomeVar = "QLIKSENSE_HOME"
qlikSenseDirVar = ".qliksense"
keepPatchFilesFlagName = "keep-config-repo-patches"
keepPatchFilesFlagUsage = "Keep config repo patch files (for debugging)"
pullFlagName = "pull"
pullFlagShorthand = "d"
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"
rootCommandName = "qliksense"
qlikSenseHomeVar = "QLIKSENSE_HOME"
qlikSenseDirVar = ".qliksense"
cleanPatchFilesFlagName = "clean"
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"
pushFlagName = "push"
pushFlagShorthand = "u"
pushFlagUsage = "If using private docker registry, push (upload) all downloaded Qliksense images to that registry before install"
rootCommandName = "qliksense"
)
func initAndExecute() error {
@@ -106,7 +105,6 @@ func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
Args: cobra.ArbitraryArgs,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if commandUsesContext(cmd.CommandPath()) {
globalEulaPreRun(cmd, p)
if err := p.SetUpQliksenseDefaultContext(); err != nil {
panic(err)
}
@@ -114,25 +112,10 @@ func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
if err := pf.Initialize(); err != nil {
panic(err)
}
globalEulaPostRun(cmd, p)
}
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
if commandUsesContext(cmd.CommandPath()) {
globalEulaPostRun(cmd, p)
}
},
SilenceUsage: true,
}
origHelpFunc := cmd.HelpFunc()
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
if !commandUsesContext(cmd.CommandPath()) {
cmd.Flags().MarkHidden("acceptEULA")
}
origHelpFunc(cmd, args)
})
accept := ""
cmd.PersistentFlags().StringVarP(&accept, "acceptEULA", "a", "", "Accept EULA for qliksense")
cmd.Flags().SetInterspersed(false)
return cmd
}
@@ -168,7 +151,9 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
// add config command
configCmd := configCmd(p)
cmd.AddCommand(configCmd)
/** disabling for now
configCmd.AddCommand(configApplyCmd(p))
**/
configCmd.AddCommand(configViewCmd(p))
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
@@ -238,34 +223,6 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
return cmd
}
func copy(src, dst string) (int64, error) {
var (
source, destination *os.File
sourceFileStat os.FileInfo
err error
nBytes int64
)
if sourceFileStat, err = os.Stat(src); err != nil {
return 0, err
}
if !sourceFileStat.Mode().IsRegular() {
return 0, fmt.Errorf("%s is not a regular file", src)
}
if source, err = os.Open(src); err != nil {
return 0, err
}
defer source.Close()
if destination, err = os.Create(dst); err != nil {
return 0, err
}
defer destination.Close()
nBytes, err = io.Copy(destination, source)
return nBytes, err
}
func levenstein(cmd *cobra.Command) {
cmd.SuggestionsMinimumDistance = 2
if len(os.Args) > 1 {

View File

@@ -130,7 +130,6 @@ In this case, the result of `qliksense about` command would display information
It supports the following flags:
- `qliksense config apply` - generate the patches and apply manifests to K8s
- `qliksense config list-contexts` - get and list contexts
- `qliksense config set` - configure a key-value pair into the current context
- `qliksense config set-configs` - set configurations into qliksense context as key-value pairs

View File

@@ -50,7 +50,7 @@ In this mode `qliksense` CLI downloads the specified version from [qliksense-k8s
The qliksense cli creates a CR for the QlikSense operator and all config operations are performed to edit the CR.
`qliksense install` or `qliksense config apply` will generate patches in local file system (i.e `~/.qliksense/contexts/<context-name>/qlik-k8s`) and
`qliksense install` will generate patches in local file system (i.e `~/.qliksense/contexts/<context-name>/qlik-k8s`) and
- Install those manifests into the cluster
- Create a custom resource (CR) for the `qliksene operator`.
@@ -68,7 +68,7 @@ qliksense config set git.repository="https://github.com/my-org/qliksense-k8s"
qliksense config set git.accessToken="<mySecretToken>"
```
When you perform `qliksense install` or `qliksene config apply`, qliksense operator performs these tasks:
When you perform `qliksense install`, qliksense operator performs these tasks:
- Download corresponding version of manifests from the your git repo
- Generate kustomize patches

4
go.mod
View File

@@ -10,7 +10,7 @@ replace (
k8s.io/client-go => k8s.io/client-go v0.17.0
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200514233516-4ac83864b7bd
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b
)
require (
@@ -40,7 +40,7 @@ require (
github.com/mitchellh/go-homedir v1.1.0
github.com/otiai10/copy v1.1.1
github.com/pkg/errors v0.9.1
github.com/qlik-oss/k-apis v0.1.4
github.com/qlik-oss/k-apis v0.1.7
github.com/robfig/cron/v3 v3.0.1
github.com/rogpeppe/go-internal v1.5.2 // indirect
github.com/spf13/cobra v0.0.6

16
go.sum
View File

@@ -471,10 +471,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=
@@ -885,12 +881,10 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/qlik-oss/k-apis v0.1.2 h1:BBcrXl+NxdsvuRsZuJbvIFxMv5QIXqWBzhXOcr5KUX8=
github.com/qlik-oss/k-apis v0.1.2/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
github.com/qlik-oss/k-apis v0.1.4 h1:YXnjKXm/yhPblzYYyVCtD0dNbIkLPLlDdBKnjeYW0IY=
github.com/qlik-oss/k-apis v0.1.4/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200514233516-4ac83864b7bd h1:dYd6duTr54L7OqykGkd3Z+336frAvzsibWNYruYkYVc=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200514233516-4ac83864b7bd/go.mod h1:zh3yFgE5zFk1kreqzVyyj1eXyIxQJT53l4zSg8Wt4SA=
github.com/qlik-oss/k-apis v0.1.7 h1:3QPymn+xMhwslm1F0oqdVqtJ/FdfAJn4vNnmS9NFIoY=
github.com/qlik-oss/k-apis v0.1.7/go.mod h1:r5hXo1mrHOzIdI0Ri9TI4SKjEXft1TZnAyJzOSm9pi0=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b h1:RDh3OZJOriy/ap1NUHVKsPG07N4DALaCzaqXFFK57T0=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b/go.mod h1:zh3yFgE5zFk1kreqzVyyj1eXyIxQJT53l4zSg8Wt4SA=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
@@ -1168,8 +1162,6 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8ou
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"time"
@@ -13,7 +12,6 @@ import (
"github.com/mitchellh/go-homedir"
appsv1 "k8s.io/api/apps/v1"
apiv1 "k8s.io/api/core/v1"
"k8s.io/api/rbac/v1beta1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@@ -26,6 +24,8 @@ import (
var gracePeriod int64 = 0
var waitTimeout = 2 * time.Minute
type ClientGoUtils struct {
Verbose bool
}
@@ -371,7 +371,6 @@ func (p *ClientGoUtils) GetPodContainerLogs(clientset kubernetes.Interface, pod
return "", err
}
defer podLogs.Close()
time.Sleep(15 * time.Second)
buf := new(bytes.Buffer)
_, err = io.Copy(buf, podLogs)
if err != nil {
@@ -382,7 +381,7 @@ func (p *ClientGoUtils) GetPodContainerLogs(clientset kubernetes.Interface, pod
}
func (p *ClientGoUtils) waitForResource(checkFunc func() (interface{}, error), validateFunc func(interface{}) bool) error {
timeout := time.NewTicker(2 * time.Minute)
timeout := time.NewTicker(waitTimeout)
defer timeout.Stop()
OUT:
for {
@@ -514,142 +513,6 @@ func (p *ClientGoUtils) WaitForDeploymentToDelete(clientset kubernetes.Interface
return err
}
func (p *ClientGoUtils) CreatePfRole(clientset kubernetes.Interface, namespace, roleName string) (*v1beta1.Role, error) {
// build the role defination we want to create
var role *v1beta1.Role
roleSpec := &v1beta1.Role{
ObjectMeta: v1.ObjectMeta{
Name: roleName,
Namespace: namespace,
Labels: map[string]string{
"app": "preflight",
},
},
Rules: []v1beta1.PolicyRule{},
}
// now create the role in kubernetes cluster using the clientset
if err := p.RetryOnError(func() (err error) {
role, err = clientset.RbacV1beta1().Roles(namespace).Create(roleSpec)
return err
}); err != nil {
return nil, err
}
p.LogVerboseMessage("Created role: %s\n", role.Name)
return role, nil
}
func (p *ClientGoUtils) DeleteRole(clientset kubernetes.Interface, namespace string, roleName string) error {
rolesClient := clientset.RbacV1beta1().Roles(namespace)
deletePolicy := v1.DeletePropagationForeground
deleteOptions := v1.DeleteOptions{
PropagationPolicy: &deletePolicy,
}
err := rolesClient.Delete(roleName, &deleteOptions)
if err != nil {
log.Printf("Error: %v\n", err)
return err
}
p.LogVerboseMessage("Deleted role: %s\n\n", roleName)
return nil
}
func (p *ClientGoUtils) CreatePfRoleBinding(clientset kubernetes.Interface, namespace, roleBindingName string) (*v1beta1.RoleBinding, error) {
var roleBinding *v1beta1.RoleBinding
// build the rolebinding defination we want to create
roleBindingSpec := &v1beta1.RoleBinding{
ObjectMeta: v1.ObjectMeta{
Name: roleBindingName,
Namespace: namespace,
Labels: map[string]string{
"app": "preflight",
},
},
Subjects: []v1beta1.Subject{
{
Kind: "ServiceAccount",
APIGroup: "",
Name: "preflight-check-subject",
Namespace: namespace,
},
},
RoleRef: v1beta1.RoleRef{
APIGroup: "",
Kind: "Role",
Name: "preflight-check-roleref",
},
}
// now create the roleBinding in kubernetes cluster using the clientset
if err := p.RetryOnError(func() (err error) {
roleBinding, err = clientset.RbacV1beta1().RoleBindings(namespace).Create(roleBindingSpec)
return err
}); err != nil {
return nil, err
}
p.LogVerboseMessage("Created RoleBinding: %s\n", roleBindingSpec.Name)
return roleBinding, nil
}
func (p *ClientGoUtils) DeleteRoleBinding(clientset kubernetes.Interface, namespace string, roleBindingName string) error {
roleBindingClient := clientset.RbacV1beta1().RoleBindings(namespace)
deletePolicy := v1.DeletePropagationForeground
deleteOptions := v1.DeleteOptions{
PropagationPolicy: &deletePolicy,
}
err := roleBindingClient.Delete(roleBindingName, &deleteOptions)
if err != nil {
log.Printf("Error: %v\n", err)
return err
}
p.LogVerboseMessage("Deleted RoleBinding: %s\n\n", roleBindingName)
return nil
}
func (p *ClientGoUtils) CreatePfServiceAccount(clientset kubernetes.Interface, namespace, serviceAccountName string) (*apiv1.ServiceAccount, error) {
var serviceAccount *apiv1.ServiceAccount
// build the serviceAccount defination we want to create
serviceAccountSpec := &apiv1.ServiceAccount{
ObjectMeta: v1.ObjectMeta{
Name: "preflight-check-test-serviceaccount",
Namespace: namespace,
Labels: map[string]string{
"app": "preflight",
},
},
}
// now create the serviceAccount in kubernetes cluster using the clientset
if err := p.RetryOnError(func() (err error) {
serviceAccount, err = clientset.CoreV1().ServiceAccounts(namespace).Create(serviceAccountSpec)
return err
}); err != nil {
return nil, err
}
p.LogVerboseMessage("Created Service Account: %s\n", serviceAccountSpec.Name)
return serviceAccount, nil
}
func (p *ClientGoUtils) DeleteServiceAccount(clientset kubernetes.Interface, namespace string, serviceAccountName string) error {
serviceAccountClient := clientset.CoreV1().ServiceAccounts(namespace)
deletePolicy := v1.DeletePropagationForeground
deleteOptions := v1.DeleteOptions{
PropagationPolicy: &deletePolicy,
}
err := serviceAccountClient.Delete(serviceAccountName, &deleteOptions)
if err != nil {
log.Printf("Error: %v\n", err)
return err
}
p.LogVerboseMessage("Deleted ServiceAccount: %s\n\n", serviceAccountName)
return nil
}
func (p *ClientGoUtils) CreatePreflightTestSecret(clientset kubernetes.Interface, namespace, secretName string, secretData []byte) (*apiv1.Secret, error) {
var secret *apiv1.Secret
var err error
@@ -755,16 +618,6 @@ func (p *ClientGoUtils) CreateStatefulSet(clientset kubernetes.Interface, namesp
return statefulset, nil
}
func (p *ClientGoUtils) GetPodsForStatefulset(clientset kubernetes.Interface, statefulset *appsv1.StatefulSet, namespace string) (*apiv1.PodList, error) {
set := labels.Set(statefulset.Spec.Template.Labels)
listOptions := v1.ListOptions{LabelSelector: set.AsSelector().String()}
pods, err := clientset.CoreV1().Pods(namespace).List(listOptions)
for _, pod := range pods.Items {
LogDebugMessage("pod: %v\n", pod.Name)
}
return pods, err
}
func (p *ClientGoUtils) waitForStatefulSet(clientset kubernetes.Interface, namespace string, pfStatefulset *appsv1.StatefulSet) error {
var err error
statefulsetName := pfStatefulset.GetName()

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,7 @@ import (
"io/ioutil"
"log"
"os"
"strings"
"github.com/qlik-oss/k-apis/pkg/config"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -40,7 +41,7 @@ func (qliksenseCR *QliksenseCR) AddCommonConfig(contextName string) {
Profile: QliksenseDefaultProfile,
RotateKeys: DefaultRotateKeys,
}
qliksenseCR.Spec.AddToSecrets("qliksense", DefaultMongodbUriKey, DefaultMongodbUri, "")
qliksenseCR.Spec.AddToSecrets("qliksense", DefaultMongodbUriKey, strings.Replace(DefaultMongodbUri, "qlik-default", contextName, 1), "")
}
// AddBaseQliksenseConfigs adds configs into config.yaml

View File

@@ -2,6 +2,7 @@ package api
import (
"reflect"
"strings"
"testing"
"github.com/qlik-oss/k-apis/pkg/config"
@@ -27,7 +28,7 @@ func TestAddCommonConfig(t *testing.T) {
Secrets: map[string]config.NameValues{
"qliksense": []config.NameValue{{
Name: DefaultMongodbUriKey,
Value: DefaultMongodbUri,
Value: strings.Replace(DefaultMongodbUri, "qlik-default", "myqliksense", 1),
},
},
},

View File

@@ -9,7 +9,7 @@ import (
"strings"
)
// KubectlApply create resoruces in the provided namespace,
// KubectlApply create resources in the provided namespace,
// if namespace="" then use whatever the kubectl default is
func KubectlApply(manifests, namespace string) error {
return kubectlOperation(manifests, "apply", namespace)
@@ -19,7 +19,7 @@ func KubectlApplyVerbose(manifests, namespace string, verbose bool) error {
return kubectlOperationVerbose(manifests, "apply", namespace, verbose)
}
// KubectlDelete delete resoruces in the provided namespace,
// KubectlDelete delete resources in the provided namespace,
// if namespace="" then use whatever the kubectl default is
func KubectlDelete(manifests, namespace string) error {
return kubectlOperation(manifests, "delete", namespace)

View File

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

View File

@@ -12,6 +12,7 @@ import (
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"time"
@@ -78,8 +79,9 @@ func ProcessConfigArgs(args []string, base64Encoded bool) ([]*ServiceKeyValue, e
if len(first) != 2 {
return nil, notValidErr
}
second := strings.SplitN(first[0], ".", 2)
if len(second) != 2 {
svcKey := getSvcAndKey(first[0])
if len(svcKey) != 2 {
return nil, notValidErr
}
resultValue := strings.Trim(first[1], "\"")
@@ -91,14 +93,33 @@ func ProcessConfigArgs(args []string, base64Encoded bool) ([]*ServiceKeyValue, e
}
}
resultSvcKV[i] = &ServiceKeyValue{
SvcName: second[0],
Key: second[1],
SvcName: svcKey[0],
Key: svcKey[1],
Value: resultValue,
}
}
return resultSvcKV, nil
}
// input should be svc[key]
func getSvcAndKey(arg string) []string {
// for key
re := regexp.MustCompile(`\[(.*)\]`)
// for service
re2 := regexp.MustCompile(`(.*)\[`)
keys := re.FindStringSubmatch(arg)
svcs := re2.FindStringSubmatch(arg)
if len(svcs) != 2 || len(keys) != 2 {
return strings.SplitN(arg, ".", 2)
}
if svcs[1] == "" || keys[1] == "" {
return []string{}
}
return []string{svcs[1], keys[1]}
}
func ExecuteTaskWithBlinkingStdoutFeedback(task func() (interface{}, error), feedback string) (result interface{}, err error) {
taskDone := make(chan bool)
go func() {

View File

@@ -45,3 +45,24 @@ func contains(arr []string, str string) bool {
}
return false
}
func TestGetSvcAndKey(t *testing.T) {
s1 := "qliksense[tls.cert]"
sa := getSvcAndKey(s1)
if sa[0] != "qliksense" || sa[1] != "tls.cert" {
t.Fail()
t.Logf("expected service: qliksense but got %s", sa[0])
t.Logf("expected key: tls.cert but got %s", sa[1])
}
s1 = "qliksense-idps.tls"
sa = getSvcAndKey(s1)
for _, s := range sa {
t.Logf("|%s|", s)
}
if sa[0] != "qliksense-idps" || sa[1] != "tls" {
t.Fail()
t.Logf("expected service: qliksense-idps but got %s", sa[0])
t.Logf("expected key: tls but got %s", sa[1])
}
}

View File

@@ -15,90 +15,90 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
out := ansi.NewColorableStdout()
// Preflight minimum kuberenetes version check
if err := qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight kubernetes minimum version check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n\n", err)
} else {
fmt.Fprintf(out, "%s\n\n", Green("Preflight kubernetes minimum version check PASSED"))
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
checkCount++
}
totalCount++
// Preflight deployment check
if err := qp.CheckDeployment(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n\n", err)
} else {
fmt.Fprintf(out, "%s\n\n", Green("Preflight deployment check PASSED"))
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
checkCount++
}
totalCount++
// Preflight service check
if err := qp.CheckService(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n\n", err)
} else {
fmt.Fprintf(out, "%s\n\n", Green("Preflight service check PASSED"))
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
checkCount++
}
totalCount++
// Preflight pod check
if err := qp.CheckPod(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n\n", err)
} else {
fmt.Fprintf(out, "%s\n\n", Green("Preflight pod check PASSED"))
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
checkCount++
}
totalCount++
// Preflight role check
if err := qp.CheckCreateRole(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n\n", err)
} else {
fmt.Fprintf(out, "%s\n\n", Green("Preflight role check PASSED"))
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
checkCount++
}
totalCount++
// Preflight rolebinding check
if err := qp.CheckCreateRoleBinding(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red(" Preflight rolebinding check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n\n", err)
} else {
fmt.Fprintf(out, "%s\n\n", Green("Preflight rolebinding check PASSED"))
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
checkCount++
}
totalCount++
// Preflight serviceaccount check
if err := qp.CheckCreateServiceAccount(namespace, false); err != nil {
fmt.Fprintf(out, "%s\n", Red(" Preflight serviceaccount check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n\n", err)
} else {
fmt.Fprintf(out, "%s\n\n", Green("Preflight serviceaccount check PASSED"))
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
checkCount++
}
totalCount++
// Preflight mongo check
if err := qp.CheckMongo(kubeConfigContents, namespace, preflightOpts, false); err != nil {
fmt.Fprintf(out, "%s\n", Red(" Preflight mongo check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n\n", err)
} else {
fmt.Fprintf(out, "%s\n\n", Green("Preflight mongo check PASSED"))
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
checkCount++
}
totalCount++
// Preflight DNS check
if err := qp.CheckDns(namespace, kubeConfigContents, false); err != nil {
fmt.Fprintf(out, "%s\n", Red(" Preflight DNS check FAILED"))
fmt.Fprintf(out, "%s\n", Red("FAILED"))
fmt.Printf("Error: %v\n\n", err)
} else {
fmt.Fprintf(out, "%s\n\n", Green("Preflight DNS check PASSED"))
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
checkCount++
}
totalCount++

View File

@@ -15,8 +15,8 @@ func (p *QliksensePreflight) CheckDeployment(namespace string, kubeConfigContent
// Deployment check
if !cleanup {
p.CG.LogVerboseMessage("Preflight deployment check: \n")
p.CG.LogVerboseMessage("--------------------------- \n")
fmt.Print("Preflight deployment check... ")
p.CG.LogVerboseMessage("\n--------------------------- \n")
}
err = p.checkPfDeployment(clientset, namespace, cleanup)
if err != nil {
@@ -38,8 +38,8 @@ func (p *QliksensePreflight) CheckService(namespace string, kubeConfigContents [
}
// Service check
if !cleanup {
p.CG.LogVerboseMessage("Preflight service check: \n")
p.CG.LogVerboseMessage("------------------------ \n")
fmt.Print("Preflight service check... ")
p.CG.LogVerboseMessage("\n------------------------ \n")
}
err = p.checkPfService(clientset, namespace, cleanup)
if err != nil {
@@ -61,8 +61,8 @@ func (p *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []byt
}
// Pod check
if !cleanup {
p.CG.LogVerboseMessage("Preflight pod check: \n")
p.CG.LogVerboseMessage("-------------------- \n")
fmt.Print("Preflight pod check... ")
p.CG.LogVerboseMessage("\n-------------------- \n")
}
err = p.checkPfPod(clientset, namespace, cleanup)
if err != nil {

View File

@@ -18,8 +18,8 @@ func (p *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byt
podName := "pf-pod-1"
if !cleanup {
p.CG.LogVerboseMessage("Preflight DNS check: \n")
p.CG.LogVerboseMessage("------------------- \n")
fmt.Print("Preflight DNS check... ")
p.CG.LogVerboseMessage("\n------------------- \n")
}
clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "")
if err != nil {

View File

@@ -22,8 +22,8 @@ const (
func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error {
if !cleanup {
qp.CG.LogVerboseMessage("Preflight mongodb check: \n")
qp.CG.LogVerboseMessage("------------------------ \n")
fmt.Print("Preflight mongodb check... ")
qp.CG.LogVerboseMessage("\n------------------------ \n")
}
var currentCR *qapi.QliksenseCR
var err error
@@ -59,10 +59,10 @@ func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace st
if !cleanup {
qp.CG.LogVerboseMessage("MongodbUrl: %s\n", preflightOpts.MongoOptions.MongodbUrl)
// if mongoDbUrl is empty, abort check
// if mongodbUrl is empty, abort check
if preflightOpts.MongoOptions.MongodbUrl == "" {
qp.CG.LogVerboseMessage("Mongodb Url is empty, hence aborting preflight check\n")
return errors.New("MongoDbUrl is empty")
return errors.New("MongodbUrl is empty")
}
}

View File

@@ -10,13 +10,6 @@ type PreflightOptions struct {
MongoOptions *MongoOptions
}
// // LogVerboseMessage logs a verbose message
// func (p *PreflightOptions) LogVerboseMessage(strMessage string, args ...interface{}) {
// if p.Verbose || os.Getenv("QLIKSENSE_DEBUG") == "true" {
// fmt.Printf(strMessage, args...)
// }
// }
type MongoOptions struct {
MongodbUrl string
CaCertFile string

View File

@@ -14,8 +14,8 @@ import (
func (qp *QliksensePreflight) CheckCreateRole(namespace string, cleanup bool) error {
// create a Role
if !cleanup {
qp.CG.LogVerboseMessage("Preflight role check: \n")
qp.CG.LogVerboseMessage("--------------------- \n")
fmt.Print("Preflight role check... ")
qp.CG.LogVerboseMessage("\n--------------------- \n")
}
err := qp.checkCreateEntity(namespace, "Role", cleanup)
if err != nil {
@@ -30,8 +30,8 @@ func (qp *QliksensePreflight) CheckCreateRole(namespace string, cleanup bool) er
func (qp *QliksensePreflight) CheckCreateRoleBinding(namespace string, cleanup bool) error {
// create a RoleBinding
if !cleanup {
qp.CG.LogVerboseMessage("Preflight rolebinding check: \n")
qp.CG.LogVerboseMessage("---------------------------- \n")
fmt.Print("Preflight rolebinding check... ")
qp.CG.LogVerboseMessage("\n---------------------------- \n")
}
err := qp.checkCreateEntity(namespace, "RoleBinding", cleanup)
if err != nil {
@@ -46,8 +46,8 @@ func (qp *QliksensePreflight) CheckCreateRoleBinding(namespace string, cleanup b
func (qp *QliksensePreflight) CheckCreateServiceAccount(namespace string, cleanup bool) error {
// create a service account
if !cleanup {
qp.CG.LogVerboseMessage("Preflight serviceaccount check: \n")
qp.CG.LogVerboseMessage("------------------------------- \n")
fmt.Print("Preflight serviceaccount check... ")
qp.CG.LogVerboseMessage("\n------------------------------- \n")
}
err := qp.checkCreateEntity(namespace, "ServiceAccount", cleanup)
if err != 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

View File

@@ -9,8 +9,8 @@ import (
)
func (p *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigContents []byte) error {
p.CG.LogVerboseMessage("Preflight kubernetes version check: \n")
p.CG.LogVerboseMessage("----------------------------------- \n")
fmt.Print("Preflight kubernetes version check... ")
p.CG.LogVerboseMessage("\n----------------------------------- \n")
var currentVersion *semver.Version
clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "")

View File

@@ -23,13 +23,12 @@ type patch struct {
Patch string `yaml:"patch"`
}
type selectivePatch struct {
type annotationTransformer struct {
APIVersion string `yaml:"apiVersion"`
Metadata struct {
Name string `yaml:"name"`
} `yaml:"metadata"`
Enabled bool `yaml:"enabled"`
Patches []patch `yaml:"patches"`
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
}
type helmChart struct {
@@ -73,7 +72,7 @@ func (q *Qliksense) About(gitRef, profile string) (*VersionOutput, error) {
}
func (q *Qliksense) AboutDir(configDirectory, profile string) (*VersionOutput, error) {
if chartVersion, err := getChartVersion(filepath.Join(configDirectory, "transformers", "qseokversion.yaml"), "qliksense"); err != nil {
if chartVersion, err := getChartVersion(filepath.Join(configDirectory, "manifests", "base", "transformers", "release", "annotations.yaml"), "app.kubernetes.io/version"); err != nil {
return nil, err
} else if kuzManifest, err := executeKustomizeBuildWithStdoutProgress(filepath.Join(configDirectory, "manifests", profile)); err != nil {
return nil, err
@@ -223,22 +222,16 @@ func traverseYamlDecodedMapRecursively(val reflect.Value, path []string, visitor
}
}
func getChartVersion(versionFile, chartName string) (string, error) {
var patchInst patch
var selPatch selectivePatch
var chart helmChart
func getChartVersion(versionFile, versionAnnotation string) (string, error) {
var annTransformer annotationTransformer
if bytes, err := ioutil.ReadFile(versionFile); err != nil {
return "", err
} else if err = yaml.Unmarshal(bytes, &selPatch); err != nil {
} else if err = yaml.Unmarshal(bytes, &annTransformer); err != nil {
return "", err
}
for _, patchInst = range selPatch.Patches {
if err := yaml.Unmarshal([]byte(patchInst.Patch), &chart); err == nil {
if chart.ChartName == chartName {
return chart.ChartVersion, nil
}
}
if version, ok := annTransformer.Annotations[versionAnnotation]; ok {
return version, nil
}
return "", nil
}

View File

@@ -1,61 +1,8 @@
package qliksense
import (
"fmt"
"io"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
)
func (q *Qliksense) ApplyCRFromReader(r io.Reader, opts *InstallCommandOptions, keepPatchFiles, 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(keepPatchFiles)
}
return q.InstallQK8s(version, opts, keepPatchFiles)
}
func IsQliksenseInstalled(crName string) bool {
args := []string{
"get",
"qliksense",
crName,
"-ogo-template",
`--template='{{ .metadata.name}}'`,
}
_, err := qapi.KubectlDirectOps(args, "")
if err != nil {
return false
}
return true
return q.InstallQK8s("", opts)
}

View File

@@ -39,9 +39,9 @@ func (q *Qliksense) ConfigApplyQK8s() error {
return errors.New(agreementTempalte + "\nPlease do $ qliksense config set-configs qliksense.acceptEULA=yes\n")
}
// create patch dependent resoruces
// create patch dependent resources
fmt.Println("Installing resources used by the kuztomize patch")
if err := q.createK8sResoruceBeforePatch(qcr); err != nil {
if err := q.createK8sResourceBeforePatch(qcr); err != nil {
return err
}

View File

@@ -358,8 +358,6 @@ func (q *Qliksense) processSetOpsRunner(arg string, cr *api.QliksenseCR) error {
cr.Spec.OpsRunner.WatchBranch = args[1]
case "image":
cr.Spec.OpsRunner.Image = args[1]
case "crPvc":
cr.Spec.OpsRunner.CrPvc = args[1]
default:
return errors.New(arg + " does not match any cr spec")
}
@@ -521,8 +519,8 @@ func (q *Qliksense) SetUpQliksenseContext(contextName string) error {
return err
}
// set the encrypted default mongo
return q.SetSecrets([]string{`qliksense.mongodbUri="mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"`}, false, false)
// set the encrypted default mongo for the context in current CR
return q.SetSecrets([]string{fmt.Sprintf("qliksense.mongodbUri=mongodb://%s-mongodb:27017/qliksense?ssl=false", contextName)}, false, false)
}
func validateInput(input string) (string, error) {

View File

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

View File

@@ -235,17 +235,17 @@ spec:
t.Fatal("expected to find the GitOps image in the list, but it wasn't there")
}
if !haveMatchingImage(func(image string) bool {
return image == "nginx"
return strings.Contains(image, "nginx")
}) {
t.Fatal("expected to find the nginx Preflight image in the list, but it wasn't there")
}
if !haveMatchingImage(func(image string) bool {
return image == "subfuzion/netcat"
return strings.Contains(image, "preflight-netcat")
}) {
t.Fatal("expected to find the netcat Preflight image in the list, but it wasn't there")
}
if !haveMatchingImage(func(image string) bool {
return image == "qlik-docker-oss.bintray.io/preflight-mongo"
return strings.Contains(image, "qlik-docker-oss.bintray.io/preflight-mongo")
}) {
t.Fatal("expected to find the preflight-mongo image in the list, but it wasn't there")
}
@@ -315,23 +315,23 @@ spec:
return err
}
transformersDir := path.Join(manifestsRootDir, "transformers")
transformersDir := path.Join(manifestsRootDir, "manifests", "base", "transformers", "release")
if err := os.MkdirAll(transformersDir, os.ModePerm); err != nil {
return err
}
if err := ioutil.WriteFile(path.Join(transformersDir, "qseokversion.yaml"), []byte(`
apiVersion: qlik.com/v1
kind: SelectivePatch
if err := ioutil.WriteFile(path.Join(transformersDir, "annotations.yaml"), []byte(`
apiVersion: builtin
kind: AnnotationsTransformer
metadata:
name: qseokversion
enabled: true
patches:
- target:
kind: HelmChart
labelSelector: name!=qliksense-init
patch: |-
chartName: qliksense
chartVersion: 1.21.23
name: common-annotations
annotations:
app.kubernetes.io/name: qliksense
app.kubernetes.io/instance: $(PREFIX)
app.kubernetes.io/version: 1.21.23
app.kubernetes.io/managed-by: qliksense-operator
fieldSpecs:
- path: metadata/annotations
create: true
`), os.ModePerm); err != nil {
return err
}

View File

@@ -138,7 +138,7 @@ func getVersion(opts *FetchCommandOptions, qcr *qapi.QliksenseCR) string {
func getVerionsOverwriteConfirmation(version string) string {
reader := bufio.NewReader(os.Stdin)
fmt.Println("The version [" + version + "] already exist")
fmt.Println("The version [" + version + "] already exists")
cfm := "n"
for {
fmt.Print("Do you want to delete and fetch again [y/N]: ")

View File

@@ -7,6 +7,9 @@ import (
"os"
"path"
"path/filepath"
"strings"
"github.com/mattn/go-tty"
"github.com/mitchellh/go-homedir"
"github.com/qlik-oss/k-apis/pkg/config"
@@ -17,34 +20,38 @@ import (
)
type InstallCommandOptions struct {
StorageClass string
MongodbUri string
RotateKeys string
DryRun bool
StorageClass string
MongodbUri string
RotateKeys string
AcceptEULA string
DryRun bool
Pull bool
Push bool
CleanPatchFiles bool
}
func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, keepPatchFiles 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 !keepPatchFiles {
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
fmt.Printf("error removing temporary changes to the config: %v\n", err)
}
}
qcr, err := qConfig.GetCurrentCR()
if err != nil {
fmt.Println("cannot get the current-context cr", err)
return err
}
if opts.AcceptEULA != "" && opts.AcceptEULA != "yes" {
enforceEula()
} else if opts.AcceptEULA == "" && !qcr.IsEULA() {
enforceEula()
}
qcr.SetEULA("yes")
if opts.MongodbUri != "" {
qcr.Spec.AddToSecrets("qliksense", "mongodbUri", opts.MongodbUri, "")
}
@@ -54,6 +61,13 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
if opts.RotateKeys != "" {
qcr.Spec.RotateKeys = opts.RotateKeys
}
if opts.CleanPatchFiles {
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
fmt.Printf("error removing temporary changes to the config: %v\n", err)
}
}
// for debugging purpose
if opts.DryRun {
// generate patches
@@ -69,7 +83,23 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
fmt.Println("error verifying whether CRDs are installed", err)
return err
} else if !installed {
return errors.New(`please install CRDs by executing: $ qliksense crds install --all`)
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 {
@@ -87,13 +117,13 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
return err
}
// create patch dependent resoruces
// create patch dependent resources
fmt.Println("Installing resources used by the kuztomize patch")
if err := q.createK8sResoruceBeforePatch(qcr); err != nil {
if err := q.createK8sResourceBeforePatch(qcr); err != nil {
return err
}
if qcr.Spec.Git != nil && qcr.Spec.Git.Repository != "" {
if qcr.Spec.OpsRunner != nil {
// fetching and applying manifest will be in the operator controller
// get decrypted cr
if dcr, err := qConfig.GetDecryptedCr(qcr); err != nil {
@@ -108,11 +138,7 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
}
}
qcr, err = qConfig.GetCurrentCR()
if err != nil {
fmt.Println("cannot get the current-context cr", err)
return err
} else if qcr.Spec.GetManifestsRoot() == "" {
if qcr.Spec.GetManifestsRoot() == "" {
return errors.New("cannot get the manifest root. Use qliksense fetch <version> or qliksense set manifestsRoot")
}
@@ -123,7 +149,7 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
return err
} else {
if IsQliksenseInstalled(dcr.GetName()) {
return q.UpgradeQK8s(keepPatchFiles)
return q.UpgradeQK8s(opts.CleanPatchFiles)
}
if err := q.applyConfigToK8s(dcr); err != nil {
fmt.Println("cannot do kubectl apply on manifests")
@@ -134,6 +160,21 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
}
}
func IsQliksenseInstalled(crName string) bool {
args := []string{
"get",
"qliksense",
crName,
"-ogo-template",
`--template='{{ .metadata.name}}'`,
}
_, err := qapi.KubectlDirectOps(args, "")
if err != nil {
return false
}
return true
}
func (q *Qliksense) getProcessedOperatorControllerString(qcr *qapi.QliksenseCR) (string, error) {
operatorControllerString := q.GetOperatorControllerString()
if imageRegistry := qcr.Spec.GetImageRegistry(); imageRegistry != "" {
@@ -207,7 +248,7 @@ func (q *Qliksense) applyCR(cr *qapi.QliksenseCR) error {
return nil
}
func (q *Qliksense) createK8sResoruceBeforePatch(qcr *qapi.QliksenseCR) error {
func (q *Qliksense) createK8sResourceBeforePatch(qcr *qapi.QliksenseCR) error {
for svc, nvs := range qcr.Spec.Secrets {
for _, nv := range nvs {
if isK8sSecretNeedToCreate(nv) {
@@ -226,3 +267,26 @@ func (q *Qliksense) createK8sResoruceBeforePatch(qcr *qapi.QliksenseCR) error {
func isK8sSecretNeedToCreate(nv config.NameValue) bool {
return nv.ValueFrom != nil
}
func enforceEula() {
fmt.Println(eulaText)
fmt.Print(eulaPrompt)
answer := readAnswerFromTty()
if strings.ToLower(answer) != "y" {
fmt.Println(eulaErrorInstruction)
os.Exit(1)
}
}
func readAnswerFromTty() string {
t, err := tty.Open()
if err != nil {
panic(err)
}
defer t.Close()
answer, err := t.ReadString()
if err != nil {
panic(err)
}
return answer
}

View File

@@ -5,7 +5,6 @@ import (
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"testing"
@@ -18,7 +17,7 @@ import (
qapi "github.com/qlik-oss/sense-installer/pkg/api"
)
func TestCreateK8sResoruceBeforePatch(t *testing.T) {
func TestCreateK8sResourceBeforePatch(t *testing.T) {
td := setup()
sampleCr := `
apiVersion: qlik.com/v1
@@ -48,15 +47,8 @@ spec:
profile: docker-desktop
rotateKeys: "yes"`
crFile := filepath.Join(testDir, "install_test.yaml")
ioutil.WriteFile(crFile, []byte(sampleCr), 0644)
q := New(testDir)
file, e := os.Open(crFile)
if e != nil {
t.Log(e)
t.FailNow()
}
if err := q.LoadCr(file, false); err != nil {
if err := q.LoadCr([]byte(sampleCr), false); err != nil {
t.Log(err)
t.FailNow()
}
@@ -66,7 +58,7 @@ spec:
t.Log(err)
t.FailNow()
}
if err = q.createK8sResoruceBeforePatch(cr); err != nil {
if err = q.createK8sResourceBeforePatch(cr); err != nil {
t.Log(err)
t.FailNow()
}

View File

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

View File

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

View File

@@ -41,7 +41,7 @@ func (q *Qliksense) getYamlFromPackrFile(packrFile string) string {
if err != nil {
fmt.Printf("Cannot read file %s", packrFile)
}
return fmt.Sprintln("#soruce: " + packrFile + "\n\n" + s + "\n---")
return fmt.Sprintln("#source: " + packrFile + "\n\n" + s + "\n---")
}
func (q *Qliksense) getFileList(resourceType string) []string {
var resList []string

View File

@@ -6,7 +6,7 @@ import (
qapi "github.com/qlik-oss/sense-installer/pkg/api"
)
func (q *Qliksense) UpgradeQK8s(keepPatchFiles bool) error {
func (q *Qliksense) UpgradeQK8s(cleanPatchFiles bool) error {
// step1: get CR
// step2: run kustomize
@@ -14,12 +14,10 @@ func (q *Qliksense) UpgradeQK8s(keepPatchFiles bool) error {
// fetch the version
qConfig := qapi.NewQConfig(q.QliksenseHome)
if !keepPatchFiles {
defer func() {
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
fmt.Printf("error removing temporary changes to the config: %v\n", err)
}
}()
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()