Compare commits
33 Commits
0.9.0
...
context_de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6070a33c2 | ||
|
|
22b9b902a9 | ||
|
|
5795988d01 | ||
|
|
449642e6f4 | ||
|
|
14b6447154 | ||
|
|
7a8926773f | ||
|
|
0b868732a7 | ||
|
|
4f2581cde2 | ||
|
|
cb78b4da9f | ||
|
|
f66a4bf245 | ||
|
|
72497d7255 | ||
|
|
b6235f20d4 | ||
|
|
93af9b4386 | ||
|
|
37fad3dbcf | ||
|
|
7a6a2b2d2b | ||
|
|
184bc6f81a | ||
|
|
140d9a6c33 | ||
|
|
68ec172226 | ||
|
|
e3c81fd717 | ||
|
|
864d186f0b | ||
|
|
a0f25848c7 | ||
|
|
9469bd8893 | ||
|
|
6ea5c3e1a8 | ||
|
|
085e718ba8 | ||
|
|
29ebf2b499 | ||
|
|
a4a7b3f0bd | ||
|
|
f1871279d0 | ||
|
|
e7b256dfd5 | ||
|
|
ddcaba4fff | ||
|
|
1eccc50e66 | ||
|
|
3638994b91 | ||
|
|
7e9dea4e5f | ||
|
|
436162f173 |
@@ -1,41 +0,0 @@
|
||||
# qliksense about
|
||||
|
||||
About action will display inside information regarding [qliksense-k8](https://github.com/qlik-oss/qliksense-k8s) release.
|
||||
|
||||
it will support following flags
|
||||
|
||||
- `qliksense about 1.0.0` display default profile for tag `1.0.0`.
|
||||
- `qliksense about 1.0.0 --profile=docker-desktop`
|
||||
- `qliksense about`
|
||||
- assuming current directory has `manifests/docker-desktop`
|
||||
- or get version information from pull of `qliksense-k8s` `master`
|
||||
|
||||
using other supported commands user might have built the CR into the location `~/.qliksense/myqliksense.yaml`
|
||||
|
||||
```yaml
|
||||
apiVersion: qlik.com/v1
|
||||
kind: QlikSense
|
||||
metadata:
|
||||
name: myqliksense
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
manifestsRoot: /Usr/ddd/my-k8-repo/manifests
|
||||
namespace: myqliksense
|
||||
storageClassName: efs
|
||||
configs:
|
||||
qliksense:
|
||||
- name: acceptEULA
|
||||
value: "yes"
|
||||
secrets:
|
||||
qliksense:
|
||||
- name: mongoDbUri
|
||||
value: "mongo://mongo:3307"
|
||||
- name: messagingPassword
|
||||
valueFromKey: messagingPassword
|
||||
```
|
||||
|
||||
In that case the command would be
|
||||
|
||||
- `qliksense about`
|
||||
- display from `/Usr/ddd/my-k8-repo/manifests/docker-desktop` location
|
||||
- pull from `master` if directory invalid/empty
|
||||
@@ -1,34 +0,0 @@
|
||||
# qliksense config
|
||||
|
||||
Config action will perform operations on configurations and contexts regarding the [qliksense-k8](https://github.com/qlik-oss/qliksense-k8s) release.
|
||||
|
||||
it will support following commands:
|
||||
|
||||
- `qliksense config apply` - generate the patchs and apply manifests to k8s
|
||||
- `qliksense config list-contexts` - retrieves the contexts and lists them
|
||||
- `qliksense config set` - configure a key value pair into the current context
|
||||
- `qliksense config set-configs` - set configurations into the qliksense context as key-value pairs
|
||||
- `qliksense config set-context` - sets the context in which the Kubernetes cluster and resources live in
|
||||
- `qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=false` - set secrets configurations into the qliksense context as key-value pairs and show encrypted value as part of CR
|
||||
- `qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=true` - set secrets configurations into the qliksense context as key-value pairs and show a key reference to the created Kubernetes secret resource as part of the CR
|
||||
- `qliksense config view` - view the qliksense operator CR
|
||||
- `qliksense config delete-context` - deletes a specific context locally (not in-cluster). Deletes context in spec of `config.yaml` and locally deletes entire folder of specified context (does not delete in-cluster secrets)
|
||||
|
||||
|
||||
the global file that abstracts all the contexts is `config.yaml`, located at: `~/.qliksense/config.yaml`:
|
||||
```yaml
|
||||
apiVersion: config.qlik.com/v1
|
||||
kind: QliksenseConfig
|
||||
metadata:
|
||||
name: QliksenseConfigMetadata
|
||||
spec:
|
||||
contexts:
|
||||
- name: qlik-default
|
||||
crFile: /Users/fff/.qliksense/contexts/qlik-default/qlik-default.yaml
|
||||
- name: myqliksense
|
||||
crFile: /Users/fff/.qliksense/contexts/myqliksense/myqliksense.yaml
|
||||
- name: hello
|
||||
crFile: /Users/fff/.qliksense/contexts/hello/hello.yaml
|
||||
currentContext: hello
|
||||
```
|
||||
|
||||
37
cmd/qliksense/apply.go
Normal file
37
cmd/qliksense/apply.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func applyCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
opts := &qliksense.InstallCommandOptions{}
|
||||
filePath := ""
|
||||
keepPatchFiles := 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(reader io.Reader) error {
|
||||
return q.ApplyCRFromReader(reader, opts, keepPatchFiles, true)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
eulaPreRunHooks.addValidator(c.Name(), loadOrApplyCommandEulaPreRunHook)
|
||||
|
||||
return c
|
||||
}
|
||||
@@ -115,15 +115,18 @@ func deleteContextConfigCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
var (
|
||||
cmd *cobra.Command
|
||||
)
|
||||
|
||||
skipConfirmation := false
|
||||
cmd = &cobra.Command{
|
||||
Use: "delete-context",
|
||||
Short: "deletes a specific context locally (not in-cluster)",
|
||||
Example: `qliksense config delete-contexts <context_name>`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return q.DeleteContextConfig(args)
|
||||
return q.DeleteContextConfig(args, skipConfirmation)
|
||||
},
|
||||
}
|
||||
f := cmd.Flags()
|
||||
|
||||
f.BoolVar(&skipConfirmation, "yes", skipConfirmation, "skips confirmation")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +1,106 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"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"
|
||||
)
|
||||
|
||||
var eulaEnforced = false
|
||||
var eulaText = "EULA text goes here..."
|
||||
var eulaPrompt = "Do you accept our EULA? (y/n): "
|
||||
var eulaErrorInstruction = "You must enter y/yes to continue"
|
||||
|
||||
func isEulaEnforced() bool {
|
||||
return eulaEnforced
|
||||
type eulaPreRunHooksT struct {
|
||||
validators map[string]func(cmd *cobra.Command, q *qliksense.Qliksense) (bool, error)
|
||||
postValidationArtifacts map[string]interface{}
|
||||
}
|
||||
|
||||
func enforceEula(q *qliksense.Qliksense) {
|
||||
if isEulaEnforced() {
|
||||
if qConfig, err := qapi.NewQConfigE(q.QliksenseHome); err != nil {
|
||||
doEnforceEula()
|
||||
} else if qcr, err := qConfig.GetCurrentCR(); err != nil || !qcr.IsEULA() {
|
||||
doEnforceEula()
|
||||
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`
|
||||
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 == "install" || commandName == "upgrade" || commandName == "apply"
|
||||
}
|
||||
|
||||
func globalEulaPreRun(cmd *cobra.Command, q *qliksense.Qliksense) {
|
||||
if isEulaEnforced(cmd.Name()) {
|
||||
if strings.TrimSpace(strings.ToLower(cmd.Flag("acceptEULA").Value.String())) != "yes" {
|
||||
if eulaPreRunHook := eulaPreRunHooks.getValidator(cmd.Name()); 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.Name()) {
|
||||
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)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
scanSuccess := scanner.Scan()
|
||||
if !scanSuccess {
|
||||
fmt.Println(eulaErrorInstruction)
|
||||
os.Exit(1)
|
||||
}
|
||||
line := scanner.Text()
|
||||
answer := strings.ToLower(strings.TrimSpace(line))
|
||||
if answer != "y" && answer != "yes" {
|
||||
answer := readRuneFromTty()
|
||||
fmt.Printf("%v\n", answer)
|
||||
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.ReadRune()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(answer)
|
||||
}
|
||||
|
||||
@@ -14,18 +14,19 @@ func installCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
Long: `install a qliksense release`,
|
||||
Example: `qliksense install <version> #if no version provides, expect manifestsRoot is set somewhere in the file system`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return q.InstallQK8s("", opts, keepPatchFiles)
|
||||
version := ""
|
||||
if len(args) != 0 {
|
||||
version = args[0]
|
||||
}
|
||||
return q.InstallQK8s(args[0], opts, keepPatchFiles)
|
||||
return q.InstallQK8s(version, opts, keepPatchFiles)
|
||||
},
|
||||
}
|
||||
|
||||
f := c.Flags()
|
||||
f.StringVarP(&opts.AcceptEULA, "acceptEULA", "a", "", "AcceptEULA for qliksense")
|
||||
f.StringVarP(&opts.StorageClass, "storageClass", "s", "", "Storage class for qliksense")
|
||||
f.StringVarP(&opts.MongoDbUri, "mongoDbUri", "m", "", "mongoDbUri for qliksense (i.e. mongodb://qlik-default-mongodb:27017/qliksense?ssl=false)")
|
||||
f.StringVarP(&opts.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)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
84
cmd/qliksense/load.go
Normal file
84
cmd/qliksense/load.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func loadCrFile(q *qliksense.Qliksense) *cobra.Command {
|
||||
filePath := ""
|
||||
overwriteExistingContext := false
|
||||
c := &cobra.Command{
|
||||
Use: "load",
|
||||
Short: "load a CR a file and create necessary structure for future use",
|
||||
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(reader io.Reader) error {
|
||||
return q.LoadCr(reader, 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(c.Name(), loadOrApplyCommandEulaPreRunHook)
|
||||
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(io.Reader) error) error {
|
||||
if crBytes := eulaPreRunHooks.getPostValidationArtifact("CR"); crBytes != nil {
|
||||
return callBack(bytes.NewBuffer(crBytes.([]byte)))
|
||||
} else {
|
||||
file, err := getCrFileFromFlag(cmd, "file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return callBack(file)
|
||||
}
|
||||
}
|
||||
@@ -4,21 +4,20 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/preflight"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func preflightCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
var configCmd = &cobra.Command{
|
||||
Use: "preflight",
|
||||
Short: "perform preflight checks on the cluster",
|
||||
Long: `perform preflight checks on the cluster`,
|
||||
Example: `qliksense preflight <preflight_check_to_run>
|
||||
Usage:
|
||||
qliksense preflight dns
|
||||
`,
|
||||
var preflightCmd = &cobra.Command{
|
||||
Use: "preflight",
|
||||
Short: "perform preflight checks on the cluster",
|
||||
Long: `perform preflight checks on the cluster`,
|
||||
Example: `qliksense preflight <preflight_check_to_run>`,
|
||||
}
|
||||
return configCmd
|
||||
return preflightCmd
|
||||
}
|
||||
|
||||
func preflightCheckDnsCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
@@ -28,14 +27,55 @@ func preflightCheckDnsCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
Long: `perform preflight dns check to check DNS connectivity status in the cluster`,
|
||||
Example: `qliksense preflight dns`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := q.DownloadPreflight()
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
err := qp.DownloadPreflight()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("There has been an error downloading preflight: %+v", err)
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
return q.CheckDns()
|
||||
return qp.CheckDns()
|
||||
},
|
||||
}
|
||||
return preflightDnsCmd
|
||||
}
|
||||
|
||||
func preflightCheckK8sVersionCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
var preflightCheckK8sVersionCmd = &cobra.Command{
|
||||
Use: "k8s-version",
|
||||
Short: "check k8s version",
|
||||
Long: `check minimum valid k8s version on the cluster`,
|
||||
Example: `qliksense preflight k8s-version`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
err := qp.DownloadPreflight()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("There has been an error downloading preflight: %+v", err)
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
return qp.CheckK8sVersion()
|
||||
},
|
||||
}
|
||||
return preflightCheckK8sVersionCmd
|
||||
}
|
||||
|
||||
func preflightAllChecksCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
var preflightAllChecksCmd = &cobra.Command{
|
||||
Use: "all",
|
||||
Short: "perform all checks",
|
||||
Long: `perform all preflight checks on the target cluster`,
|
||||
Example: `qliksense preflight all`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
err := qp.DownloadPreflight()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("There has been an error downloading preflight: %+v", err)
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
return qp.RunAllPreflightChecks()
|
||||
},
|
||||
}
|
||||
return preflightAllChecksCmd
|
||||
}
|
||||
|
||||
@@ -78,51 +78,59 @@ func setUpPaths() (string, error) {
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the version number of qliksense cli",
|
||||
Long: `All software has versions. This is Hugo's`,
|
||||
Long: "Print the version number of qliksense cli",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("%s (%s, %s)\n", pkg.Version, pkg.Commit, pkg.CommitDate)
|
||||
},
|
||||
}
|
||||
|
||||
func commandUsesContext(command string) bool {
|
||||
return command != "" && command != "help" && command != "version"
|
||||
func commandUsesContext(commandName string) bool {
|
||||
return commandName != "" && commandName != "qliksense" && commandName != "help" && commandName != "version"
|
||||
}
|
||||
|
||||
func globalPreRun(cmd *cobra.Command, p *qliksense.Qliksense) {
|
||||
if command := cmd.CalledAs(); commandUsesContext(command) {
|
||||
if isEulaEnforced() {
|
||||
enforceEula(p)
|
||||
}
|
||||
|
||||
if err := p.SetUpQliksenseDefaultContext(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if isEulaEnforced() {
|
||||
if err := p.SetEulaAccepted(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "qliksense",
|
||||
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) {
|
||||
globalPreRun(cmd, p)
|
||||
if commandUsesContext(cmd.Name()) {
|
||||
globalEulaPreRun(cmd, p)
|
||||
if err := p.SetUpQliksenseDefaultContext(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
globalEulaPostRun(cmd, p)
|
||||
}
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
if commandUsesContext(cmd.Name()) {
|
||||
globalEulaPostRun(cmd, p)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
origHelpFunc := cmd.HelpFunc()
|
||||
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
|
||||
if !commandUsesContext(cmd.Name()) {
|
||||
cmd.Flags().MarkHidden("acceptEULA")
|
||||
}
|
||||
origHelpFunc(cmd, args)
|
||||
})
|
||||
accept := ""
|
||||
cmd.PersistentFlags().StringVarP(&accept, "acceptEULA", "a", "", "Accept EULA for qliksense")
|
||||
cmd.Flags().SetInterspersed(false)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
viper.SetEnvPrefix("QLIKSENSE")
|
||||
viper.AutomaticEnv()
|
||||
}
|
||||
|
||||
func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
cmd := getRootCmd(p)
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
// For qliksense overrides/commands
|
||||
|
||||
cmd.AddCommand(getInstallableVersionsCmd(p))
|
||||
cmd.AddCommand(pullQliksenseImages(p))
|
||||
cmd.AddCommand(pushQliksenseImages(p))
|
||||
@@ -188,19 +196,17 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
// add preflight command
|
||||
preflightCmd := preflightCmd(p)
|
||||
preflightCmd.AddCommand(preflightCheckDnsCmd(p))
|
||||
preflightCmd.AddCommand(preflightCheckK8sVersionCmd(p))
|
||||
preflightCmd.AddCommand(preflightAllChecksCmd(p))
|
||||
//preflightCmd.AddCommand(preflightCheckMongoCmd(p))
|
||||
//preflightCmd.AddCommand(preflightCheckAllCmd(p))
|
||||
|
||||
cmd.AddCommand(preflightCmd)
|
||||
|
||||
cmd.AddCommand(loadCrFile(p))
|
||||
cmd.AddCommand((applyCmd(p)))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
viper.SetEnvPrefix("QLIKSENSE")
|
||||
viper.AutomaticEnv()
|
||||
}
|
||||
|
||||
func copy(src, dst string) (int64, error) {
|
||||
var (
|
||||
source, destination *os.File
|
||||
|
||||
119
docs/command_reference.md
Normal file
119
docs/command_reference.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# qliksense command reference
|
||||
|
||||
## qliksense apply
|
||||
|
||||
`qliksense apply` command takes input a cr file or input from pipe
|
||||
|
||||
- `qliksense apply -f cr-file.yaml`
|
||||
- `cat cr-file.yaml | qliksense apply -f -`
|
||||
|
||||
the content of `cr-file.yaml` should be something similar
|
||||
|
||||
```yaml
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
metadata:
|
||||
name: qlik-test
|
||||
labels:
|
||||
version: v0.0.2
|
||||
spec:
|
||||
configs:
|
||||
qliksense:
|
||||
- name: acceptEULA
|
||||
value: "yes"
|
||||
secrets:
|
||||
qliksense:
|
||||
- name: mongoDbUri
|
||||
value: mongodb://qlik-test-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
```
|
||||
|
||||
This will do everything `qliksense load` does and install the qliksense into the cluster.
|
||||
|
||||
## qliksense load
|
||||
|
||||
`qliksense load` command takes input a cr file or input from pipe.
|
||||
|
||||
- `qliksense load -f cr-file.yaml`
|
||||
- `cat cr-file.yaml | qliksense load -f -`
|
||||
|
||||
This will load the CR into `${QLIKSENSE_HOME}` folder, create context structure and set the current context to that CR.
|
||||
This will also encrypt the secrets from CR while writing the CR into the disk.
|
||||
|
||||
## qliksense about
|
||||
|
||||
About action will display inside information regarding [qliksense-k8](https://github.com/qlik-oss/qliksense-k8s) release.
|
||||
|
||||
it will support following flags
|
||||
|
||||
- `qliksense about 1.0.0` display default profile for tag `1.0.0`.
|
||||
- `qliksense about 1.0.0 --profile=docker-desktop`
|
||||
- `qliksense about`
|
||||
- assuming current directory has `manifests/docker-desktop`
|
||||
- or get version information from pull of `qliksense-k8s` `master`
|
||||
|
||||
using other supported commands user might have built the CR into the location `~/.qliksense/myqliksense.yaml`
|
||||
|
||||
```yaml
|
||||
apiVersion: qlik.com/v1
|
||||
kind: QlikSense
|
||||
metadata:
|
||||
name: myqliksense
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
manifestsRoot: /Usr/ddd/my-k8-repo/manifests
|
||||
namespace: myqliksense
|
||||
storageClassName: efs
|
||||
configs:
|
||||
qliksense:
|
||||
- name: acceptEULA
|
||||
value: "yes"
|
||||
secrets:
|
||||
qliksense:
|
||||
- name: mongoDbUri
|
||||
value: "mongo://mongo:3307"
|
||||
- name: messagingPassword
|
||||
valueFromKey: messagingPassword
|
||||
```
|
||||
|
||||
In that case the command would be
|
||||
|
||||
- `qliksense about`
|
||||
- display from `/Usr/ddd/my-k8-repo/manifests/docker-desktop` location
|
||||
- pull from `master` if directory invalid/empty
|
||||
|
||||
|
||||
## qliksense config
|
||||
|
||||
Config action will perform operations on configurations and contexts regarding the [qliksense-k8](https://github.com/qlik-oss/qliksense-k8s) release.
|
||||
|
||||
it will support following commands:
|
||||
|
||||
- `qliksense config apply` - generate the patchs and apply manifests to k8s
|
||||
- `qliksense config list-contexts` - retrieves the contexts and lists them
|
||||
- `qliksense config set` - configure a key value pair into the current context
|
||||
- `qliksense config set-configs` - set configurations into the qliksense context as key-value pairs
|
||||
- `qliksense config set-context` - sets the context in which the Kubernetes cluster and resources live in
|
||||
- `qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=false` - set secrets configurations into the qliksense context as key-value pairs and show encrypted value as part of CR
|
||||
- `qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=true` - set secrets configurations into the qliksense context as key-value pairs and show a key reference to the created Kubernetes secret resource as part of the CR
|
||||
- `qliksense config view` - view the qliksense operator CR
|
||||
- `qliksense config delete-context` - deletes a specific context locally (not in-cluster). Deletes context in spec of `config.yaml` and locally deletes entire folder of specified context (does not delete in-cluster secrets)
|
||||
|
||||
|
||||
the global file that abstracts all the contexts is `config.yaml`, located at: `~/.qliksense/config.yaml`:
|
||||
```yaml
|
||||
apiVersion: config.qlik.com/v1
|
||||
kind: QliksenseConfig
|
||||
metadata:
|
||||
name: QliksenseConfigMetadata
|
||||
spec:
|
||||
contexts:
|
||||
- name: qlik-default
|
||||
crFile: /Users/fff/.qliksense/contexts/qlik-default/qlik-default.yaml
|
||||
- name: myqliksense
|
||||
crFile: /Users/fff/.qliksense/contexts/myqliksense/myqliksense.yaml
|
||||
- name: hello
|
||||
crFile: /Users/fff/.qliksense/contexts/hello/hello.yaml
|
||||
currentContext: hello
|
||||
```
|
||||
@@ -95,36 +95,3 @@ spec:
|
||||
....
|
||||
```
|
||||
|
||||
##Preflight checks
|
||||
Preflight checks provide pre-installation cluster conformance testing and validation before we install qliksense on the cluster. We gather a suite of conformance tests that can be easily written and run on the target cluster to verify that cluster-specific requirements are met.
|
||||
The suite consists of a set of `collectors` which run the specifications of every test and `analyzers` which analyze the results of every test run by the collector.
|
||||
We support the following tests at the moment as part of preflight checks, and the range of the suite will be expanded in future.
|
||||
|
||||
### DNS check
|
||||
Run the following command to view help about the commands supported by preflight at any moment:
|
||||
```console
|
||||
qliksense preflight
|
||||
perform preflight checks on the cluster
|
||||
|
||||
Usage:
|
||||
qliksense preflight [command]
|
||||
|
||||
Examples:
|
||||
qliksense preflight <preflight_check_to_run>
|
||||
|
||||
Usage:
|
||||
qliksense preflight dns
|
||||
|
||||
Available Commands:
|
||||
dns perform preflight dns check
|
||||
```
|
||||
|
||||
Run the following command to perform preflight DNS check. The expected output is also shown below.
|
||||
```console
|
||||
qliksense preflight dns
|
||||
Running Preflight checks ⠧
|
||||
--- PASS DNS check
|
||||
--- DNS check passed
|
||||
--- PASS cluster-preflight-checks
|
||||
PASS
|
||||
```
|
||||
|
||||
100
docs/preflight_checks.md
Normal file
100
docs/preflight_checks.md
Normal file
@@ -0,0 +1,100 @@
|
||||
##Preflight checks
|
||||
Preflight checks provide pre-installation cluster conformance testing and validation before we install qliksense on the cluster. We gather a suite of conformance tests that can be easily written and run on the target cluster to verify that cluster-specific requirements are met.
|
||||
The suite consists of a set of `collectors` which run the specifications of every test and `analyzers` which analyze the results of every test run by the collector.
|
||||
We support the following tests at the moment as part of preflight checks, and the range of the suite will be expanded in future.
|
||||
|
||||
Run the following command to view help about the commands supported by preflight at any moment:
|
||||
```console
|
||||
$ qliksense preflight
|
||||
perform preflight checks on the cluster
|
||||
|
||||
Usage:
|
||||
qliksense preflight [command]
|
||||
|
||||
Examples:
|
||||
qliksense preflight <preflight_check_to_run>
|
||||
|
||||
Available Commands:
|
||||
all perform all checks
|
||||
dns perform preflight dns check
|
||||
k8s-version check k8s version
|
||||
|
||||
Flags:
|
||||
-h, --help help for preflight
|
||||
```
|
||||
|
||||
### DNS check
|
||||
Run the following command to perform preflight DNS check. We setup a kubernetes deployment and try to reach it as part of establishing DNS connectivity in this check.
|
||||
The expected output should be similar to the one shown below.
|
||||
```console
|
||||
$ qliksense preflight dns
|
||||
|
||||
Creating resources to run preflight checks
|
||||
deployment.apps/qnginx001 created
|
||||
service/qnginx001 created
|
||||
pod/qnginx001-6db5fc95c5-s9sl2 condition met
|
||||
Running Preflight checks ⠇
|
||||
--- PASS DNS check
|
||||
--- DNS check passed
|
||||
--- PASS cluster-preflight-checks
|
||||
PASS
|
||||
|
||||
DNS check completed, cleaning up resources now
|
||||
service "qnginx001" deleted
|
||||
deployment.extensions "qnginx001" deleted
|
||||
|
||||
```
|
||||
|
||||
### Kubernetes version check
|
||||
We check the version of the target kubernetes cluster and ensure that it falls in the valid range of kubernetes versions that are supported by qliksense.
|
||||
The command to run this check and the expected similar output are as shown below:
|
||||
```console
|
||||
$ qliksense preflight k8s-version
|
||||
|
||||
Minimum Kubernetes version supported: 1.11.0
|
||||
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-13T18:08:14Z", GoVersion:"go1.13.8", Compiler:"gc", Platform:"darwin/amd64"}
|
||||
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:07:57Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
|
||||
Running Preflight checks ⠇
|
||||
--- PASS Required Kubernetes Version
|
||||
--- Good to go.
|
||||
--- PASS cluster-preflight-checks
|
||||
PASS
|
||||
|
||||
Minimum kubernetes version check completed
|
||||
```
|
||||
|
||||
### Running all checks
|
||||
Run the command shown below to execute all preflight checks.
|
||||
```console
|
||||
$ qliksense preflight all
|
||||
|
||||
Running all preflight checks
|
||||
|
||||
Running DNS check...
|
||||
Creating resources to run preflight checks
|
||||
deployment.apps/qnginx001 created
|
||||
service/qnginx001 created
|
||||
pod/qnginx001-6db5fc95c5-grwv2 condition met
|
||||
Running Preflight checks ⠇
|
||||
--- PASS DNS check
|
||||
--- DNS check passed
|
||||
--- PASS cluster-preflight-checks
|
||||
PASS
|
||||
|
||||
DNS check completed, cleaning up resources now
|
||||
service "qnginx001" deleted
|
||||
deployment.extensions "qnginx001" deleted
|
||||
|
||||
Running minimum kubernetes version check...
|
||||
Minimum Kubernetes version supported: 1.11.0
|
||||
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-13T18:08:14Z", GoVersion:"go1.13.8", Compiler:"gc", Platform:"darwin/amd64"}
|
||||
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:07:57Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
|
||||
Running Preflight checks ⠧
|
||||
--- PASS Required Kubernetes Version
|
||||
--- Good to go.
|
||||
--- PASS cluster-preflight-checks
|
||||
PASS
|
||||
|
||||
Minimum kubernetes version check completed
|
||||
Completed running all preflight checks
|
||||
```
|
||||
1
go.mod
1
go.mod
@@ -37,6 +37,7 @@ require (
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.4
|
||||
github.com/mattn/go-tty v0.0.3
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
|
||||
4
go.sum
4
go.sum
@@ -696,9 +696,13 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/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.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/go-shellwords v1.0.6 h1:9Jok5pILi5S1MnDirGVTufYGtksUs/V2BWUP3ZkeUUI=
|
||||
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI=
|
||||
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
|
||||
@@ -17,6 +17,8 @@ markdown_extensions:
|
||||
nav:
|
||||
- Overview: index.md
|
||||
- getting_started.md
|
||||
- command_reference.md
|
||||
- concepts.md
|
||||
- preflight_checks.md
|
||||
- air_gap.md
|
||||
- Releases ⧉: https://github.com/qlik-oss/sense-installer/releases
|
||||
|
||||
170
pkg/api/apis.go
170
pkg/api/apis.go
@@ -46,19 +46,20 @@ func NewQConfigE(qsHome string) (*QliksenseConfig, error) {
|
||||
qc.QliksenseHomePath = qsHome
|
||||
return qc, nil
|
||||
}
|
||||
func NewQConfigEmpty(qsHome string) *QliksenseConfig {
|
||||
return &QliksenseConfig{
|
||||
QliksenseHomePath: qsHome,
|
||||
}
|
||||
}
|
||||
|
||||
// GetCR create a QliksenseCR object for a particular context
|
||||
// from file ~/.qliksense/contexts/<contx-name>/<contx-name>.yaml
|
||||
func (qc *QliksenseConfig) GetCR(contextName string) (*QliksenseCR, error) {
|
||||
crFilePath := qc.getCRFilePath(contextName)
|
||||
crFilePath := qc.GetCRFilePath(contextName)
|
||||
if crFilePath == "" {
|
||||
return nil, errors.New("context name " + contextName + " not found")
|
||||
}
|
||||
return getCRObject(crFilePath)
|
||||
}
|
||||
|
||||
func getUnencryptedCR() {
|
||||
|
||||
return qc.GetAndTransformCrObject(crFilePath)
|
||||
}
|
||||
|
||||
// GetCurrentCR create a QliksenseCR object for current context
|
||||
@@ -67,14 +68,14 @@ func (qc *QliksenseConfig) GetCurrentCR() (*QliksenseCR, error) {
|
||||
}
|
||||
|
||||
// SetCrLocation sets the CR location for a context. Helpful during test
|
||||
func (qc *QliksenseConfig) SetCrLocation(contextName, filepath string) (*QliksenseConfig, error) {
|
||||
func (qc *QliksenseConfig) SetCrLocation(contextName, filePath string) (*QliksenseConfig, error) {
|
||||
tempQc := &QliksenseConfig{}
|
||||
copier.Copy(tempQc, qc)
|
||||
found := false
|
||||
tempQc.Spec.Contexts = []Context{}
|
||||
for _, c := range qc.Spec.Contexts {
|
||||
if c.Name == contextName {
|
||||
c.CrFile = filepath
|
||||
c.CrFile = filePath
|
||||
found = true
|
||||
}
|
||||
tempQc.Spec.Contexts = append(tempQc.Spec.Contexts, []Context{c}...)
|
||||
@@ -85,7 +86,8 @@ func (qc *QliksenseConfig) SetCrLocation(contextName, filepath string) (*Qliksen
|
||||
return nil, errors.New("cannot find the context")
|
||||
}
|
||||
|
||||
func getCRObject(crfile string) (*QliksenseCR, error) {
|
||||
// GetCRObject create a qliksense CR object from file
|
||||
func GetCRObject(crfile string) (*QliksenseCR, error) {
|
||||
cr := &QliksenseCR{}
|
||||
err := ReadFromFile(cr, crfile)
|
||||
if err != nil {
|
||||
@@ -96,11 +98,36 @@ func getCRObject(crfile string) (*QliksenseCR, error) {
|
||||
return cr, nil
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) getCRFilePath(contextName string) string {
|
||||
func (qc *QliksenseConfig) GetAndTransformCrObject(crfile string) (*QliksenseCR, error) {
|
||||
cr, err := GetCRObject(crfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cr.Spec.ManifestsRoot != "" && !filepath.IsAbs(cr.Spec.ManifestsRoot) {
|
||||
cr.Spec.ManifestsRoot = filepath.Join(qc.QliksenseHomePath, cr.Spec.ManifestsRoot)
|
||||
}
|
||||
return cr, nil
|
||||
}
|
||||
|
||||
//CreateCRObjectFromString create a QliksenseCR from string content
|
||||
func CreateCRObjectFromString(crContent string) (*QliksenseCR, error) {
|
||||
if crContent == "" {
|
||||
return nil, errors.New("empty string cannot qliksensecr")
|
||||
}
|
||||
cr := &QliksenseCR{}
|
||||
err := ReadFromStream(cr, strings.NewReader(crContent))
|
||||
if err != nil {
|
||||
fmt.Println("cannot unmarshal cr ", err)
|
||||
return nil, err
|
||||
}
|
||||
return cr, nil
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) GetCRFilePath(contextName string) string {
|
||||
crFilePath := ""
|
||||
for _, ctx := range qc.Spec.Contexts {
|
||||
if ctx.Name == contextName {
|
||||
crFilePath = ctx.CrFile
|
||||
crFilePath = filepath.Join(qc.QliksenseHomePath, ctx.CrFile)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -125,7 +152,7 @@ func (qc *QliksenseConfig) BuildRepoPath(version string) string {
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) BuildRepoPathForContext(contextName, version string) string {
|
||||
return filepath.Join(qc.QliksenseHomePath, qliksenseContextsDirName, contextName, "qlik-k8s", version)
|
||||
return filepath.Join(qc.GetContextPath(contextName), "qlik-k8s", version)
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) BuildCurrentManifestsRoot(version string) string {
|
||||
@@ -133,13 +160,59 @@ func (qc *QliksenseConfig) BuildCurrentManifestsRoot(version string) string {
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) WriteCR(cr *QliksenseCR, contextName string) error {
|
||||
crf := qc.getCRFilePath(contextName)
|
||||
crf := qc.GetCRFilePath(contextName)
|
||||
if crf == "" {
|
||||
return errors.New("context name " + contextName + " not found")
|
||||
}
|
||||
return WriteToFile(cr, crf)
|
||||
|
||||
return qc.TransformAndWriteCr(cr, crf)
|
||||
}
|
||||
|
||||
//CreateOrWriteCrAndContext create necessary folder structure, update config.yaml and context yaml files
|
||||
func (qc *QliksenseConfig) CreateOrWriteCrAndContext(cr *QliksenseCR) error {
|
||||
if qc.QliksenseHomePath == "" {
|
||||
return errors.New("qliksense home is not set")
|
||||
}
|
||||
crf := qc.GetCRFilePath(cr.GetName())
|
||||
if crf == "" {
|
||||
// create direcotry structure for context
|
||||
cDir := filepath.Join(qc.QliksenseHomePath, "contexts", cr.GetName())
|
||||
if err := os.MkdirAll(cDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
crf = filepath.Join(cDir, cr.GetName()+".yaml")
|
||||
ctx := Context{
|
||||
Name: cr.GetName(),
|
||||
CrFile: filepath.Join("contexts", cr.GetName(), cr.GetName()+".yaml"),
|
||||
}
|
||||
qc.AddToContexts(ctx)
|
||||
|
||||
if err := qc.Write(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return qc.TransformAndWriteCr(cr, crf)
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) TransformAndWriteCr(cr *QliksenseCR, file string) error {
|
||||
if strings.HasPrefix(cr.Spec.ManifestsRoot, qc.QliksenseHomePath) {
|
||||
cr.Spec.ManifestsRoot = strings.Replace(cr.Spec.ManifestsRoot, qc.QliksenseHomePath+"/", "", 1)
|
||||
}
|
||||
if err := WriteToFile(cr, file); err != nil {
|
||||
return err
|
||||
}
|
||||
if cr.Spec.ManifestsRoot != "" {
|
||||
cr.Spec.ManifestsRoot = filepath.Join(qc.QliksenseHomePath, cr.Spec.ManifestsRoot)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (qc *QliksenseConfig) AddToContexts(ctx Context) error {
|
||||
//TODO: additional duplicate check may be added latter
|
||||
qc.Spec.Contexts = append(qc.Spec.Contexts, ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
func (qc *QliksenseConfig) WriteCurrentContextCR(cr *QliksenseCR) error {
|
||||
return qc.WriteCR(cr, qc.Spec.CurrentContext)
|
||||
}
|
||||
@@ -230,6 +303,15 @@ func (qc *QliksenseConfig) getDockerConfigJsonSecret(name string) (*DockerConfig
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) getCurrentContextEncryptionKeyPairLocation() (string, error) {
|
||||
|
||||
if qcr, err := qc.GetCurrentCR(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return qc.getContextEncryptionKeyPairLocation(qcr.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) getContextEncryptionKeyPairLocation(contextName string) (string, error) {
|
||||
// Check env var: QLIKSENSE_KEY_LOCATION to determine location to store keypair
|
||||
var secretKeyPairLocation string
|
||||
if os.Getenv("QLIKSENSE_KEY_LOCATION") != "" {
|
||||
@@ -238,13 +320,9 @@ func (qc *QliksenseConfig) getCurrentContextEncryptionKeyPairLocation() (string,
|
||||
} else {
|
||||
// QLIKSENSE_KEY_LOCATION has not been set, hence storing key pair in default location:
|
||||
// /.qliksense/secrets/contexts/<current-context>/secrets/
|
||||
if qcr, err := qc.GetCurrentCR(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
secretKeyPairLocation = filepath.Join(qc.QliksenseHomePath, qliksenseSecretsDirName, qliksenseContextsDirName, qcr.GetObjectMeta().GetName(), qliksenseSecretsDirName)
|
||||
}
|
||||
secretKeyPairLocation = filepath.Join(qc.QliksenseHomePath, qliksenseSecretsDirName, qliksenseContextsDirName, contextName, qliksenseSecretsDirName)
|
||||
|
||||
}
|
||||
LogDebugMessage("SecretKeyLocation to store key pair: %s", secretKeyPairLocation)
|
||||
return secretKeyPairLocation, nil
|
||||
}
|
||||
|
||||
@@ -261,7 +339,15 @@ func (qc *QliksenseConfig) GetCurrentContextEjsonKeyDir() (string, error) {
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) GetCurrentContextEncryptionKeyPair() (*rsa.PublicKey, *rsa.PrivateKey, error) {
|
||||
secretKeyPairLocation, err := qc.getCurrentContextEncryptionKeyPairLocation()
|
||||
if qcr, err := qc.GetCurrentCR(); err != nil {
|
||||
return nil, nil, err
|
||||
} else {
|
||||
return qc.GetContextEncryptionKeyPair(qcr.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) GetContextEncryptionKeyPair(contextName string) (*rsa.PublicKey, *rsa.PrivateKey, error) {
|
||||
secretKeyPairLocation, err := qc.getContextEncryptionKeyPairLocation(contextName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -386,3 +472,45 @@ func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error)
|
||||
newCr.Spec.Secrets = finalSecrets
|
||||
return newCr, nil
|
||||
}
|
||||
|
||||
//Validate validate CR
|
||||
func (cr *QliksenseCR) Validate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
//CreateContextDirs create context dir structure ~/.qliksense/contexts/contextName
|
||||
func (qc *QliksenseConfig) CreateContextDirs(contextName string) error {
|
||||
return os.MkdirAll(qc.GetContextPath(contextName), os.ModePerm)
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) GetContextPath(contextName string) string {
|
||||
return filepath.Join(qc.QliksenseHomePath, qliksenseContextsDirName, contextName)
|
||||
}
|
||||
|
||||
//BuildCrFileAbsolutePath build absolute path for a cr ie. ~/.qliksense/contexts/qlik-defautl/qlik-default.yaml
|
||||
func (qc *QliksenseConfig) BuildCrFileAbsolutePath(contextName string) string {
|
||||
return filepath.Join(qc.GetContextPath(contextName), contextName+".yaml")
|
||||
}
|
||||
|
||||
//BuildCrFilePath build cr file path i.e. contexts/qlik-default/qlik-default.yaml
|
||||
func (qc *QliksenseConfig) BuildCrFilePath(contextName string) string {
|
||||
return filepath.Join(qc.GetContextPath(contextName), contextName+".yaml")
|
||||
}
|
||||
|
||||
//AddToContexts add the context into qc.Spec.Contexts
|
||||
func (qc *QliksenseConfig) AddToContextsRaw(crName, crFile string) {
|
||||
qc.Spec.Contexts = append(qc.Spec.Contexts, []Context{
|
||||
{CrFile: crFile,
|
||||
Name: crName},
|
||||
}...)
|
||||
}
|
||||
|
||||
//SetCurrentContextName set the qc.Spec.CurrentContext
|
||||
func (qc *QliksenseConfig) SetCurrentContextName(name string) {
|
||||
qc.Spec.CurrentContext = name
|
||||
}
|
||||
|
||||
//Write write QliksenseConfig into config.yaml
|
||||
func (qc *QliksenseConfig) Write() error {
|
||||
return WriteToFile(qc, filepath.Join(qc.QliksenseHomePath, "config.yaml"))
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func TestGetCR(t *testing.T) {
|
||||
// create CR
|
||||
createCRFile(dir)
|
||||
|
||||
crFile := filepath.Join(dir, "contexts", "contx1", "contx1.yaml")
|
||||
crFile := filepath.Join("contexts", "contx1", "contx1.yaml")
|
||||
qct, e := qc.SetCrLocation("contx1", crFile)
|
||||
if e != nil {
|
||||
t.Fail()
|
||||
@@ -100,7 +100,7 @@ func TestGetDecryptedCr(t *testing.T) {
|
||||
// create CR
|
||||
createCRFile(dir)
|
||||
|
||||
crFile := filepath.Join(dir, "contexts", "contx1", "contx1.yaml")
|
||||
crFile := filepath.Join("contexts", "contx1", "contx1.yaml")
|
||||
qct, e := qc.SetCrLocation("contx1", crFile)
|
||||
if e != nil {
|
||||
t.Fail()
|
||||
|
||||
@@ -3,8 +3,10 @@ package api
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/qlik-oss/k-apis/pkg/config"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@@ -103,14 +105,22 @@ func ReadFromFile(content interface{}, sourceFile string) error {
|
||||
if content == nil || sourceFile == "" {
|
||||
return nil
|
||||
}
|
||||
contents, err := ioutil.ReadFile(sourceFile)
|
||||
file, e := os.Open(sourceFile)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return ReadFromStream(content, file)
|
||||
}
|
||||
|
||||
// ReadFromStream reads from input stream and creat yaml struct of type content
|
||||
func ReadFromStream(content interface{}, reader io.Reader) error {
|
||||
contents, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("There was an error reading from file: %s, %v", sourceFile, err)
|
||||
err = fmt.Errorf("There was an error reading from reader: %v", err)
|
||||
return err
|
||||
}
|
||||
// reading k8s style object
|
||||
// https://stackoverflow.com/questions/44306554/how-to-deserialize-kubernetes-yaml-file
|
||||
dec := machine_yaml.NewYAMLOrJSONDecoder(bytes.NewReader(contents), 10000)
|
||||
dec.Decode(content)
|
||||
return nil
|
||||
return dec.Decode(content)
|
||||
}
|
||||
|
||||
@@ -232,7 +232,6 @@ func UntarGzFile(destination, fileToUntar string) error {
|
||||
fileAtLoc.Chmod(os.ModePerm)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UnZipFile(destination, fileToUnzip string) error {
|
||||
|
||||
16
pkg/preflight/all_checks.go
Normal file
16
pkg/preflight/all_checks.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package preflight
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (qp *QliksensePreflight) RunAllPreflightChecks() error {
|
||||
//run all preflight checks
|
||||
fmt.Println("Running all preflight checks")
|
||||
fmt.Printf("\nRunning DNS check...\n")
|
||||
qp.CheckDns()
|
||||
fmt.Printf("\nRunning minimum kubernetes version check...\n")
|
||||
qp.CheckK8sVersion()
|
||||
fmt.Println("Completed running all preflight checks")
|
||||
return nil
|
||||
}
|
||||
128
pkg/preflight/dns_check.go
Normal file
128
pkg/preflight/dns_check.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package preflight
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
const dnsCheckYAML = `
|
||||
apiVersion: troubleshoot.replicated.com/v1beta1
|
||||
kind: Preflight
|
||||
metadata:
|
||||
name: cluster-preflight-checks
|
||||
namespace: {{ . }}
|
||||
spec:
|
||||
collectors:
|
||||
- run:
|
||||
collectorName: spin-up-pod
|
||||
args: ["-z", "-v", "-w 1", "qnginx001", "80"]
|
||||
command: ["nc"]
|
||||
image: subfuzion/netcat:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: spin-up-pod-check-dns
|
||||
namespace: {{ . }}
|
||||
timeout: 30s
|
||||
|
||||
analyzers:
|
||||
- textAnalyze:
|
||||
checkName: DNS check
|
||||
collectorName: spin-up-pod-check-dns
|
||||
fileName: spin-up-pod.txt
|
||||
regex: succeeded
|
||||
outcomes:
|
||||
- fail:
|
||||
message: DNS check failed
|
||||
- pass:
|
||||
message: DNS check passed
|
||||
`
|
||||
|
||||
func (qp *QliksensePreflight) CheckDns() error {
|
||||
// retrieve namespace
|
||||
namespace := api.GetKubectlNamespace()
|
||||
|
||||
api.LogDebugMessage("Namespace: %s\n", namespace)
|
||||
|
||||
tmpl, err := template.New("dnsCheckYAML").Parse(dnsCheckYAML)
|
||||
if err != nil {
|
||||
fmt.Printf("cannot parse template: %v", err)
|
||||
return err
|
||||
}
|
||||
tempYaml, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
fmt.Printf("cannot create file: %v", err)
|
||||
return err
|
||||
}
|
||||
api.LogDebugMessage("Temp Yaml file: %s\n", tempYaml.Name())
|
||||
|
||||
b := bytes.Buffer{}
|
||||
err = tmpl.Execute(&b, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
tempYaml.WriteString(b.String())
|
||||
|
||||
// creating Kubectl resources
|
||||
appName := "qnginx001"
|
||||
const PreflightChecksDirName = "preflight_checks"
|
||||
|
||||
fmt.Println("Creating resources to run preflight checks")
|
||||
|
||||
// kubectl create deployment
|
||||
opr := fmt.Sprintf("create deployment %s --image=nginx", appName)
|
||||
err = initiateK8sOps(opr, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Deleting deployment..
|
||||
opr = fmt.Sprintf("delete deployment %s", appName)
|
||||
// we want to delete the k8s resource here, we dont care a lot about an error here
|
||||
_ = initiateK8sOps(opr, namespace)
|
||||
api.LogDebugMessage("delete deployment executed")
|
||||
}()
|
||||
|
||||
// create service
|
||||
opr = fmt.Sprintf("create service clusterip %s --tcp=80:80", appName)
|
||||
err = initiateK8sOps(opr, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// delete service
|
||||
opr = fmt.Sprintf("delete service %s", appName)
|
||||
// we want to delete the k8s resource here, we dont care a lot about an error here
|
||||
_ = initiateK8sOps(opr, namespace)
|
||||
api.LogDebugMessage("delete service executed")
|
||||
}()
|
||||
|
||||
//kubectl -n $namespace wait --for=condition=ready pod -l app=$appName --timeout=120s
|
||||
opr = fmt.Sprintf("wait --for=condition=ready pod -l app=%s --timeout=120s", appName)
|
||||
err = initiateK8sOps(opr, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
api.LogDebugMessage("kubectl wait executed")
|
||||
|
||||
// call preflight
|
||||
preflightCommand := filepath.Join(qp.Q.QliksenseHome, PreflightChecksDirName, preflightFileName)
|
||||
|
||||
err = invokePreflight(preflightCommand, tempYaml)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
fmt.Println("DNS check completed, cleaning up resources now")
|
||||
return nil
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
package qliksense
|
||||
package preflight
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -11,11 +10,15 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"text/template"
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
type QliksensePreflight struct {
|
||||
Q *qliksense.Qliksense
|
||||
}
|
||||
|
||||
const (
|
||||
// preflight releases have the same version
|
||||
preflightRelease = "v0.9.26"
|
||||
@@ -23,45 +26,15 @@ const (
|
||||
preflightMacFile = "preflight_darwin_amd64.tar.gz"
|
||||
preflightWindowsFile = "preflight_windows_amd64.zip"
|
||||
PreflightChecksDirName = "preflight_checks"
|
||||
preflightFileName = "preflight"
|
||||
)
|
||||
|
||||
var preflightBaseURL = fmt.Sprintf("https://github.com/replicatedhq/troubleshoot/releases/download/%s/", preflightRelease)
|
||||
|
||||
const dnsCheckYAML = `
|
||||
apiVersion: troubleshoot.replicated.com/v1beta1
|
||||
kind: Preflight
|
||||
metadata:
|
||||
name: cluster-preflight-checks
|
||||
namespace: {{ . }}
|
||||
spec:
|
||||
collectors:
|
||||
- run:
|
||||
collectorName: spin-up-pod
|
||||
args: ["-z", "-v", "-w 1", "qnginx001", "80"]
|
||||
command: ["nc"]
|
||||
image: subfuzion/netcat:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: spin-up-pod-check-dns
|
||||
namespace: {{ . }}
|
||||
timeout: 30s
|
||||
|
||||
analyzers:
|
||||
- textAnalyze:
|
||||
checkName: DNS check
|
||||
collectorName: spin-up-pod-check-dns
|
||||
fileName: spin-up-pod.txt
|
||||
regex: succeeded
|
||||
outcomes:
|
||||
- fail:
|
||||
message: DNS check failed
|
||||
- pass:
|
||||
message: DNS check passed
|
||||
`
|
||||
|
||||
func (q *Qliksense) DownloadPreflight() error {
|
||||
func (qp *QliksensePreflight) DownloadPreflight() error {
|
||||
const preflightExecutable = "preflight"
|
||||
|
||||
preflightInstallDir := filepath.Join(q.QliksenseHome, PreflightChecksDirName)
|
||||
preflightInstallDir := filepath.Join(qp.Q.QliksenseHome, PreflightChecksDirName)
|
||||
platform := runtime.GOOS
|
||||
|
||||
exists, err := checkInstalled(preflightInstallDir, preflightExecutable)
|
||||
@@ -72,7 +45,7 @@ func (q *Qliksense) DownloadPreflight() error {
|
||||
}
|
||||
if exists {
|
||||
// preflight exist, no need to download again.
|
||||
api.LogDebugMessage("Preflight already exist, proceeding to perform checks")
|
||||
api.LogDebugMessage("Preflight already exists, proceeding to perform checks")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -160,93 +133,6 @@ func determinePlatformSpecificUrls(platform string) (string, string, error) {
|
||||
return preflightUrl, preflightFile, nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) CheckDns() error {
|
||||
// retrieve namespace
|
||||
namespace := api.GetKubectlNamespace()
|
||||
|
||||
api.LogDebugMessage("Namespace: %s\n", namespace)
|
||||
|
||||
tmpl, err := template.New("test").Parse(dnsCheckYAML)
|
||||
if err != nil {
|
||||
fmt.Printf("cannot parse template: %v", err)
|
||||
return err
|
||||
}
|
||||
tempYaml, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
fmt.Printf("cannot create file: %v", err)
|
||||
return err
|
||||
}
|
||||
api.LogDebugMessage("Temp Yaml file: %s\n", tempYaml.Name())
|
||||
|
||||
b := bytes.Buffer{}
|
||||
err = tmpl.Execute(&b, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
tempYaml.WriteString(b.String())
|
||||
|
||||
// creating Kubectl resources
|
||||
appName := "qnginx001"
|
||||
const PreflightChecksDirName = "preflight_checks"
|
||||
const preflightFileName = "preflight"
|
||||
|
||||
fmt.Println("Creating resources to run preflight checks")
|
||||
|
||||
// kubectl create deployment
|
||||
opr := fmt.Sprintf("create deployment %s --image=nginx", appName)
|
||||
err = initiateK8sOps(opr, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Deleting deployment..
|
||||
opr = fmt.Sprintf("delete deployment %s", appName)
|
||||
// we want to delete the k8s resource here, we dont care a lot about an error here
|
||||
_ = initiateK8sOps(opr, namespace)
|
||||
api.LogDebugMessage("delete deployment executed")
|
||||
}()
|
||||
|
||||
// create service
|
||||
opr = fmt.Sprintf("create service clusterip %s --tcp=80:80", appName)
|
||||
err = initiateK8sOps(opr, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// delete service
|
||||
opr = fmt.Sprintf("delete service %s", appName)
|
||||
// we want to delete the k8s resource here, we dont care a lot about an error here
|
||||
_ = initiateK8sOps(opr, namespace)
|
||||
api.LogDebugMessage("delete service executed")
|
||||
}()
|
||||
|
||||
//kubectl -n $namespace wait --for=condition=ready pod -l app=$appName --timeout=120s
|
||||
opr = fmt.Sprintf("wait --for=condition=ready pod -l app=%s --timeout=120s", appName)
|
||||
err = initiateK8sOps(opr, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
api.LogDebugMessage("kubectl wait executed")
|
||||
|
||||
// call preflight
|
||||
preflightCommand := filepath.Join(q.QliksenseHome, PreflightChecksDirName, preflightFileName)
|
||||
|
||||
err = invokePreflight(preflightCommand, tempYaml)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initiateK8sOps(opr, namespace string) error {
|
||||
opr1 := strings.Fields(opr)
|
||||
err := api.KubectlDirectOps(opr1, namespace)
|
||||
@@ -296,6 +182,6 @@ func invokePreflight(preflightCommand string, yamlFile *os.File) error {
|
||||
// break
|
||||
// }
|
||||
//}
|
||||
fmt.Println("Preflight checks completed, cleaning up resources now")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package qliksense
|
||||
package preflight
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
84
pkg/preflight/version_check.go
Normal file
84
pkg/preflight/version_check.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package preflight
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
const minK8sVersion = "1.11.0"
|
||||
const checkVersionYAML = `
|
||||
apiVersion: troubleshoot.replicated.com/v1beta1
|
||||
kind: Preflight
|
||||
metadata:
|
||||
name: cluster-preflight-checks
|
||||
namespace: {{ .namespace }}
|
||||
spec:
|
||||
analyzers:
|
||||
- clusterVersion:
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "< {{ .minK8sVersion }}"
|
||||
message: The application requires at least Kubernetes {{ .minK8sVersion }} or later.
|
||||
uri: https://www.kubernetes.io
|
||||
- pass:
|
||||
when: ">= {{ .minK8sVersion }}"
|
||||
message: Good to go.
|
||||
`
|
||||
|
||||
func (qp *QliksensePreflight) CheckK8sVersion() error {
|
||||
// retrieve namespace
|
||||
namespace := api.GetKubectlNamespace()
|
||||
|
||||
api.LogDebugMessage("Namespace: %s\n", namespace)
|
||||
|
||||
tmpl, err := template.New("checkVersionYAML").Parse(checkVersionYAML)
|
||||
if err != nil {
|
||||
fmt.Printf("cannot parse template: %v", err)
|
||||
return err
|
||||
}
|
||||
tempYaml, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
fmt.Printf("cannot create file: %v", err)
|
||||
return err
|
||||
}
|
||||
api.LogDebugMessage("Temp Yaml file: %s\n", tempYaml.Name())
|
||||
|
||||
b := bytes.Buffer{}
|
||||
err = tmpl.Execute(&b, map[string]string{
|
||||
"namespace": namespace,
|
||||
"minK8sVersion": minK8sVersion,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
tempYaml.WriteString(b.String())
|
||||
//api.LogDebugMessage("Temp yaml contents: %s", b.String())
|
||||
fmt.Printf("Minimum Kubernetes version supported: %s\n", minK8sVersion)
|
||||
|
||||
// current kubectl version
|
||||
opr := fmt.Sprintf("version")
|
||||
err = initiateK8sOps(opr, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// call preflight
|
||||
preflightCommand := filepath.Join(qp.Q.QliksenseHome, PreflightChecksDirName, preflightFileName)
|
||||
|
||||
err = invokePreflight(preflightCommand, tempYaml)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Minimum kubernetes version check completed")
|
||||
return nil
|
||||
}
|
||||
22
pkg/qliksense/apply.go
Normal file
22
pkg/qliksense/apply.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func (q *Qliksense) ApplyCRFromReader(r io.Reader, opts *InstallCommandOptions, keepPatchFiles, overwriteExistingContext bool) error {
|
||||
if err := q.LoadCr(r, overwriteExistingContext); err != nil {
|
||||
return err
|
||||
}
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
cr, err := qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := q.InstallQK8s(cr.GetLabelFromCr("version"), opts, keepPatchFiles); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
24
pkg/qliksense/confirmation.go
Normal file
24
pkg/qliksense/confirmation.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func AskForConfirmation(s string) bool {
|
||||
for {
|
||||
fmt.Printf("%s [y/n]: ", s)
|
||||
var response string
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if strings.EqualFold(response, "y") || strings.EqualFold(response, "yes") {
|
||||
return true
|
||||
} else if strings.EqualFold(response, "n") || strings.EqualFold(response, "no") {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,7 +276,7 @@ func (q *Qliksense) SetOtherConfigs(args []string) error {
|
||||
// SetContextConfig - set the context for qliksense kubernetes resources to live in
|
||||
func (q *Qliksense) SetContextConfig(args []string) error {
|
||||
if len(args) == 1 {
|
||||
err := q.SetUpQliksenseContext(args[0], false)
|
||||
err := q.SetUpQliksenseContext(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -301,7 +301,7 @@ func (q *Qliksense) ListContextConfigs() error {
|
||||
w.Flush()
|
||||
if len(qliksenseConfig.Spec.Contexts) > 0 {
|
||||
for _, cont := range qliksenseConfig.Spec.Contexts {
|
||||
fmt.Fprintln(w, cont.Name, "\t", cont.CrFile, "\t")
|
||||
fmt.Fprintln(w, cont.Name, "\t", qliksenseConfig.GetCRFilePath(cont.Name), "\t")
|
||||
}
|
||||
w.Flush()
|
||||
fmt.Fprintln(out, "")
|
||||
@@ -312,7 +312,7 @@ func (q *Qliksense) ListContextConfigs() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) DeleteContextConfig(args []string) error {
|
||||
func (q *Qliksense) DeleteContextConfig(args []string, flag bool) error {
|
||||
if len(args) == 1 {
|
||||
qliksenseConfigFile := filepath.Join(q.QliksenseHome, QliksenseConfigFile)
|
||||
var qliksenseConfig api.QliksenseConfig
|
||||
@@ -354,9 +354,17 @@ func (q *Qliksense) DeleteContextConfig(args []string) error {
|
||||
}
|
||||
newLength := len(qliksenseConfig.Spec.Contexts)
|
||||
if currentLength != newLength {
|
||||
api.WriteToFile(&qliksenseConfig, qliksenseConfigFile)
|
||||
fmt.Fprintln(out, chalk.Yellow.Color(chalk.Underline.TextStyle("Warning: Active resources may still be running in-cluster")))
|
||||
fmt.Fprintln(out, chalk.Green.Color("Successfully deleted context: "), chalk.Bold.TextStyle(args[0]))
|
||||
ans := flag
|
||||
if ans == false {
|
||||
ans = AskForConfirmation("Are You Sure? ")
|
||||
}
|
||||
if ans == true {
|
||||
api.WriteToFile(&qliksenseConfig, qliksenseConfigFile)
|
||||
fmt.Fprintln(out, chalk.Yellow.Color(chalk.Underline.TextStyle("Warning: Active resources may still be running in-cluster")))
|
||||
fmt.Fprintln(out, chalk.Green.Color("Successfully deleted context: "), chalk.Bold.TextStyle(args[0]))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
err := fmt.Errorf(chalk.Red.Color("Context not found"))
|
||||
return err
|
||||
@@ -374,11 +382,11 @@ func (q *Qliksense) DeleteContextConfig(args []string) error {
|
||||
|
||||
// SetUpQliksenseDefaultContext - to setup dir structure for default qliksense context
|
||||
func (q *Qliksense) SetUpQliksenseDefaultContext() error {
|
||||
return q.SetUpQliksenseContext(DefaultQliksenseContext, true)
|
||||
return q.SetUpQliksenseContext(DefaultQliksenseContext)
|
||||
}
|
||||
|
||||
// SetUpQliksenseContext - to setup qliksense context
|
||||
func (q *Qliksense) SetUpQliksenseContext(contextName string, isDefaultContext bool) error {
|
||||
func (q *Qliksense) SetUpQliksenseContext(contextName string) error {
|
||||
if contextName == "" {
|
||||
err := fmt.Errorf("Please enter a non-empty context-name")
|
||||
log.Println(err)
|
||||
@@ -392,83 +400,29 @@ func (q *Qliksense) SetUpQliksenseContext(contextName string, isDefaultContext b
|
||||
}
|
||||
|
||||
qliksenseConfigFile := filepath.Join(q.QliksenseHome, QliksenseConfigFile)
|
||||
var qliksenseConfig api.QliksenseConfig
|
||||
configFileTrack := false
|
||||
qliksenseConfig := api.NewQConfigEmpty(q.QliksenseHome)
|
||||
|
||||
if !api.FileExists(qliksenseConfigFile) {
|
||||
qliksenseConfig.AddBaseQliksenseConfigs(contextName)
|
||||
} else {
|
||||
if err := api.ReadFromFile(&qliksenseConfig, qliksenseConfigFile); err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
if isDefaultContext { // if config file exits but a default context is requested, we want to prevent writing to config file
|
||||
configFileTrack = true
|
||||
}
|
||||
}
|
||||
// creating a file in the name of the context if it does not exist/ opening it to append/modify content if it already exists
|
||||
|
||||
qliksenseContextsDir1 := filepath.Join(q.QliksenseHome, QliksenseContextsDir)
|
||||
if !api.DirExists(qliksenseContextsDir1) {
|
||||
if err := os.Mkdir(qliksenseContextsDir1, os.ModePerm); err != nil {
|
||||
err = fmt.Errorf("Not able to create %s dir: %v", qliksenseContextsDir1, err)
|
||||
if err := api.ReadFromFile(qliksenseConfig, qliksenseConfigFile); err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
api.LogDebugMessage("%s exists", qliksenseContextsDir1)
|
||||
|
||||
// creating contexts/qlik-default/qlik-default.yaml file
|
||||
qliksenseContextFile := filepath.Join(qliksenseContextsDir1, contextName, contextName+".yaml")
|
||||
//var qliksenseCR api.QliksenseCR
|
||||
|
||||
defaultContextsDir := filepath.Join(qliksenseContextsDir1, contextName)
|
||||
if !api.DirExists(defaultContextsDir) {
|
||||
if err := os.Mkdir(defaultContextsDir, os.ModePerm); err != nil {
|
||||
err = fmt.Errorf("Not able to create %s: %v", defaultContextsDir, err)
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
api.LogDebugMessage("%s exists", defaultContextsDir)
|
||||
if !api.FileExists(qliksenseContextFile) {
|
||||
qliksenseCR := &api.QliksenseCR{}
|
||||
qliksenseCR.AddCommonConfig(contextName)
|
||||
api.WriteToFile(&qliksenseCR, qliksenseContextFile)
|
||||
api.LogDebugMessage("Added Context: %s", contextName)
|
||||
}
|
||||
// else {
|
||||
// if err := api.ReadFromFile(&qliksenseCR, qliksenseContextFile); err != nil {
|
||||
// log.Println(err)
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
//api.WriteToFile(&qliksenseCR, qliksenseContextFile)
|
||||
ctxTrack := false
|
||||
if len(qliksenseConfig.Spec.Contexts) > 0 {
|
||||
for _, ctx := range qliksenseConfig.Spec.Contexts {
|
||||
if ctx.Name == contextName {
|
||||
ctx.CrFile = qliksenseContextFile
|
||||
ctxTrack = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ctxTrack {
|
||||
qliksenseConfig.Spec.Contexts = append(qliksenseConfig.Spec.Contexts, api.Context{
|
||||
Name: contextName,
|
||||
CrFile: qliksenseContextFile,
|
||||
})
|
||||
if qliksenseConfig.IsContextExist(contextName) {
|
||||
return nil
|
||||
}
|
||||
qliksenseCR := &api.QliksenseCR{}
|
||||
qliksenseCR.AddCommonConfig(contextName)
|
||||
qliksenseConfig.Spec.CurrentContext = contextName
|
||||
if !configFileTrack {
|
||||
api.WriteToFile(&qliksenseConfig, qliksenseConfigFile)
|
||||
if err := qliksenseConfig.CreateOrWriteCrAndContext(qliksenseCR); err != nil {
|
||||
return err
|
||||
}
|
||||
// set the encrypted default mongo
|
||||
q.SetSecrets([]string{`qliksense.mongoDbUri="mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"`}, false)
|
||||
|
||||
return nil
|
||||
// set the encrypted default mongo
|
||||
return q.SetSecrets([]string{`qliksense.mongoDbUri="mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"`}, false)
|
||||
}
|
||||
|
||||
func validateInput(input string) (string, error) {
|
||||
@@ -586,6 +540,9 @@ func (q *Qliksense) SetEulaAccepted() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
qcr.SetEULA("yes")
|
||||
return qConfig.WriteCurrentContextCR(qcr)
|
||||
if !qcr.IsEULA() {
|
||||
qcr.SetEULA("yes")
|
||||
return qConfig.WriteCurrentContextCR(qcr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -152,6 +152,7 @@ func removePrivateKey() {
|
||||
|
||||
func setup() func() {
|
||||
// create tests dir
|
||||
os.RemoveAll(testDir)
|
||||
if err := os.Mkdir(testDir, 0777); err != nil {
|
||||
log.Printf("\nError occurred: %v", err)
|
||||
}
|
||||
@@ -164,7 +165,7 @@ metadata:
|
||||
spec:
|
||||
contexts:
|
||||
- name: qlik-default
|
||||
crFile: /root/.qliksense/contexts/qlik-default.yaml
|
||||
crFile: contexts/qlik-default/qlik-default.yaml
|
||||
currentContext: qlik-default
|
||||
`
|
||||
configFile := filepath.Join(testDir, "config.yaml")
|
||||
@@ -274,7 +275,7 @@ func TestSetUpQliksenseContext(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
q := New(tt.args.qlikSenseHome)
|
||||
if err := q.SetUpQliksenseContext(tt.args.contextName, tt.args.isDefaultContext); (err != nil) != tt.wantErr {
|
||||
if err := q.SetUpQliksenseContext(tt.args.contextName); (err != nil) != tt.wantErr {
|
||||
t.Errorf("SetUpQliksenseContext() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
@@ -396,7 +397,7 @@ func TestSetConfigs(t *testing.T) {
|
||||
|
||||
func TestSetImageRegistry(t *testing.T) {
|
||||
getQlikSense := func(tmpQlikSenseHome string) (*Qliksense, error) {
|
||||
if err := ioutil.WriteFile(path.Join(tmpQlikSenseHome, "config.yaml"), []byte(fmt.Sprintf(`
|
||||
if err := ioutil.WriteFile(path.Join(tmpQlikSenseHome, "config.yaml"), []byte(`
|
||||
apiVersion: config.qlik.com/v1
|
||||
kind: QliksenseConfig
|
||||
metadata:
|
||||
@@ -404,9 +405,9 @@ metadata:
|
||||
spec:
|
||||
contexts:
|
||||
- name: qlik-default
|
||||
crFile: %s/contexts/qlik-default/qlik-default.yaml
|
||||
crFile: contexts/qlik-default/qlik-default.yaml
|
||||
currentContext: qlik-default
|
||||
`, tmpQlikSenseHome)), os.ModePerm); err != nil {
|
||||
`), os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -798,11 +799,11 @@ metadata:
|
||||
spec:
|
||||
contexts:
|
||||
- name: qlik-default
|
||||
crFile: /root/.qliksense/contexts/qlik-default.yaml
|
||||
crFile: contexts/qlik-default.yaml
|
||||
- name: qlik1
|
||||
crFile: /root/.qliksense/contexts/qlik1.yaml
|
||||
crFile: contexts/qlik1.yaml
|
||||
- name: qlik2
|
||||
crFile: /root/.qliksense/contexts/qlik2.yaml
|
||||
crFile: contexts/qlik2.yaml
|
||||
currentContext: qlik1
|
||||
`
|
||||
configFile := filepath.Join(testDir, "config.yaml")
|
||||
@@ -927,9 +928,10 @@ func TestDeleteContexts(t *testing.T) {
|
||||
q := New(tt.args.qlikSenseHome)
|
||||
var arg []string
|
||||
arg = append(arg, tt.args.contextName)
|
||||
if err := q.DeleteContextConfig(arg); (err != nil) != tt.wantErr {
|
||||
if err := q.DeleteContextConfig(arg, true); (err != nil) != tt.wantErr {
|
||||
t.Errorf("DeleteContext() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ func Test_Pull_Push_ImagesForCurrentCR(t *testing.T) {
|
||||
}
|
||||
|
||||
func setupQlikSenseHome(t *testing.T, tmpQlikSenseHome string, registry *testRegistryV2, clientAuth clientAuthType) error {
|
||||
if err := ioutil.WriteFile(path.Join(tmpQlikSenseHome, "config.yaml"), []byte(fmt.Sprintf(`
|
||||
if err := ioutil.WriteFile(path.Join(tmpQlikSenseHome, "config.yaml"), []byte(`
|
||||
apiVersion: config.qlik.com/v1
|
||||
kind: QliksenseConfig
|
||||
metadata:
|
||||
@@ -179,9 +179,9 @@ metadata:
|
||||
spec:
|
||||
contexts:
|
||||
- name: qlik-default
|
||||
crFile: %s/contexts/qlik-default/qlik-default.yaml
|
||||
crFile: contexts/qlik-default/qlik-default.yaml
|
||||
currentContext: qlik-default
|
||||
`, tmpQlikSenseHome)), os.ModePerm); err != nil {
|
||||
`), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
35
pkg/qliksense/fetch_test.go
Normal file
35
pkg/qliksense/fetch_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func TestFetchAndUpdateCR(t *testing.T) {
|
||||
tempHome, _ := ioutil.TempDir("", "")
|
||||
|
||||
q := &Qliksense{
|
||||
QliksenseHome: tempHome,
|
||||
}
|
||||
q.SetUpQliksenseContext("test1")
|
||||
qConfig := qapi.NewQConfig(tempHome)
|
||||
if err := fetchAndUpdateCR(qConfig, "v0.0.2"); err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualCrFile := filepath.Join(tempHome, "contexts", "test1", "test1.yaml")
|
||||
cr := &qapi.QliksenseCR{}
|
||||
if err := qapi.ReadFromFile(cr, actualCrFile); err != nil {
|
||||
t.Log(err)
|
||||
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")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
type InstallCommandOptions struct {
|
||||
AcceptEULA string
|
||||
StorageClass string
|
||||
MongoDbUri string
|
||||
RotateKeys string
|
||||
@@ -41,9 +40,7 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.AcceptEULA != "" {
|
||||
qcr.SetEULA(opts.AcceptEULA)
|
||||
}
|
||||
qcr.SetEULA("yes")
|
||||
if opts.MongoDbUri != "" {
|
||||
qcr.Spec.AddToSecrets("qliksense", "mongoDbUri", opts.MongoDbUri, "")
|
||||
}
|
||||
|
||||
65
pkg/qliksense/install_test.go
Normal file
65
pkg/qliksense/install_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func TestCreateK8sResoruceBeforePatch(t *testing.T) {
|
||||
td := setup()
|
||||
sampleCr := `
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
metadata:
|
||||
name: qlik-test3
|
||||
labels:
|
||||
version: v0.0.2
|
||||
spec:
|
||||
git:
|
||||
repository: https://github.com/ffoysal/qliksense-k8s
|
||||
accessToken: abababababababaab
|
||||
userName: "blblbl"
|
||||
gitOps:
|
||||
enabled: "no"
|
||||
schedule: "*/1 * * * *"
|
||||
watchBranch: pr-branch-db1d26d6
|
||||
image: qlik-docker-oss.bintray.io/qliksense-repo-watcher
|
||||
configs:
|
||||
qliksense:
|
||||
- name: acceptEULA
|
||||
value: "yes"
|
||||
secrets:
|
||||
qliksense:
|
||||
- name: mongoDbUri
|
||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
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 {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
qConfig := qapi.NewQConfig(testDir)
|
||||
cr, err := qConfig.GetCR("qlik-test3")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if err = q.createK8sResoruceBeforePatch(cr); err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
td()
|
||||
}
|
||||
81
pkg/qliksense/load_cr.go
Normal file
81
pkg/qliksense/load_cr.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
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 {
|
||||
return err
|
||||
} else {
|
||||
fmt.Println("cr name: [ " + crName + " ] has been loaded")
|
||||
}
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
if qConfig.IsContextExist(cr.GetName()) {
|
||||
if !overwriteExistingContext {
|
||||
return "", errors.New("Context with name: " + cr.GetName() + " already exists. " +
|
||||
"Please delete the existing context first using the delete-context command or specify the --overwrite flag.")
|
||||
} else if err := os.RemoveAll(qConfig.GetContextPath(cr.GetName())); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if err := qConfig.CreateContextDirs(cr.GetName()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// encrypt the secrets and do base64 then update the CR
|
||||
rsaPublicKey, _, err := qConfig.GetContextEncryptionKeyPair(cr.GetName())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for svc, nvs := range cr.Spec.Secrets {
|
||||
for _, nv := range nvs {
|
||||
if nv.ValueFrom == nil {
|
||||
skv := &qapi.ServiceKeyValue{
|
||||
Key: nv.Name,
|
||||
Value: nv.Value,
|
||||
SvcName: svc,
|
||||
}
|
||||
if err := q.processSecret(skv, rsaPublicKey, cr, false); err != nil {
|
||||
return cr.GetName(), err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write to disk
|
||||
|
||||
if err = qConfig.CreateOrWriteCrAndContext(cr); err != nil {
|
||||
return "", err
|
||||
}
|
||||
qConfig.SetCurrentContextName(cr.GetName())
|
||||
qConfig.Write()
|
||||
|
||||
return cr.GetName(), nil
|
||||
}
|
||||
140
pkg/qliksense/load_cr_test.go
Normal file
140
pkg/qliksense/load_cr_test.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func TestLoadCrFile(t *testing.T) {
|
||||
td := setup()
|
||||
setup()
|
||||
sampleCr1 := `
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
metadata:
|
||||
name: qlik-test
|
||||
labels:
|
||||
version: v0.0.2
|
||||
spec:
|
||||
git:
|
||||
repository: https://github.com/ffoysal/qliksense-k8s
|
||||
accessToken: abababababababaab
|
||||
userName: "blblbl"
|
||||
gitOps:
|
||||
enabled: "no"
|
||||
schedule: "*/1 * * * *"
|
||||
watchBranch: pr-branch-db1d26d6
|
||||
image: qlik-docker-oss.bintray.io/qliksense-repo-watcher
|
||||
configs:
|
||||
qliksense:
|
||||
- name: acceptEULA
|
||||
value: "yes"
|
||||
secrets:
|
||||
qliksense:
|
||||
- name: mongoDbUri
|
||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"`
|
||||
sampleCr2 := `
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
metadata:
|
||||
name: qlik-test3
|
||||
labels:
|
||||
version: v0.0.2
|
||||
spec:
|
||||
git:
|
||||
repository: https://github.com/ffoysal/qliksense-k8s
|
||||
accessToken: abababababababaab
|
||||
userName: "blblbl"
|
||||
gitOps:
|
||||
enabled: "no"
|
||||
schedule: "*/1 * * * *"
|
||||
watchBranch: pr-branch-db1d26d6
|
||||
image: qlik-docker-oss.bintray.io/qliksense-repo-watcher
|
||||
configs:
|
||||
qliksense:
|
||||
- name: acceptEULA
|
||||
value: "yes"
|
||||
secrets:
|
||||
qliksense:
|
||||
- name: mongoDbUri
|
||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"`
|
||||
|
||||
duplicateCr := `
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
metadata:
|
||||
name: qlik-default
|
||||
labels:
|
||||
version: v0.0.2
|
||||
spec:
|
||||
git:
|
||||
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 {
|
||||
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 {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
qConfig := qapi.NewQConfig(testDir)
|
||||
cr, err := qConfig.GetCR("qlik-test")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if cr.GetName() != "qlik-test" {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
cr, err = qConfig.GetCR("qlik-test3")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if cr.GetName() != "qlik-test3" {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
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 {
|
||||
t.FailNow()
|
||||
}
|
||||
td()
|
||||
}
|
||||
Reference in New Issue
Block a user