Compare commits
26 Commits
v0.31.0
...
renovate/g
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92652f7fae | ||
|
|
6ab9317638 | ||
|
|
5899760c16 | ||
|
|
a63c400106 | ||
|
|
568012edd8 | ||
|
|
34f702b183 | ||
|
|
d8cdbb30bb | ||
|
|
616e759089 | ||
|
|
c2d3f39542 | ||
|
|
0b6c1b8bc9 | ||
|
|
7091da099c | ||
|
|
1f8bcf3469 | ||
|
|
627a4a14f9 | ||
|
|
66333e9e97 | ||
|
|
36c91e9dab | ||
|
|
b6ae0c9873 | ||
|
|
6899a7be77 | ||
|
|
373d6499dc | ||
|
|
746823c54b | ||
|
|
73b5da8c14 | ||
|
|
7cc6e55779 | ||
|
|
27c8f3ee8e | ||
|
|
cb9f463f01 | ||
|
|
293e923c82 | ||
|
|
504190c75c | ||
|
|
fc62e5fea4 |
2
.github/workflows/mkdocs.yml
vendored
2
.github/workflows/mkdocs.yml
vendored
@@ -16,6 +16,6 @@ jobs:
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Deploy docs
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@1.11
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@1.12
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -19,7 +19,7 @@ func about(q *qliksense.Qliksense) *cobra.Command {
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: "about ref",
|
||||
Short: "Displays information pertaining to Qliksense on Kubernetes",
|
||||
Short: "Displays information pertaining to qliksense on Kubernetes",
|
||||
Long: "Gives the version of QLik Sense on Kubernetes and versions of images.",
|
||||
Example: `
|
||||
qliksense about 1.0.0
|
||||
|
||||
@@ -24,7 +24,6 @@ func applyCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
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(&opts.CleanPatchFiles, cleanPatchFilesFlagName, opts.CleanPatchFiles, cleanPatchFilesFlagUsage)
|
||||
f.BoolVarP(&opts.Pull, pullFlagName, pullFlagShorthand, opts.Pull, pullFlagUsage)
|
||||
f.BoolVarP(&opts.Push, pushFlagName, pushFlagShorthand, opts.Push, pushFlagUsage)
|
||||
|
||||
@@ -34,8 +34,8 @@ func crdsInstallCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
}
|
||||
c := &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "Install CRDs for Qliksense application. Use install --all=false to exclude the operator CRD",
|
||||
Long: "Install CRDs for Qliksense application. Use install --all=false to exclude the operator CRD",
|
||||
Short: "Install CRDs for qliksense application. Use install --all=false to exclude the operator CRD",
|
||||
Long: "Install CRDs for qliksense application. Use install --all=false to exclude the operator CRD",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return q.InstallCrds(opts)
|
||||
},
|
||||
|
||||
@@ -24,10 +24,17 @@ func installCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
}
|
||||
|
||||
if filePath != "" {
|
||||
return apply(q, cmd, opts)
|
||||
if err := apply(q, cmd, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return q.InstallQK8s(version, opts)
|
||||
if err := q.InstallQK8s(version, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
postflightChecksCmd := AllPostflightChecks(q)
|
||||
postflightChecksCmd.DisableFlagParsing = true
|
||||
return postflightChecksCmd.Execute()
|
||||
},
|
||||
}
|
||||
|
||||
@@ -35,7 +42,6 @@ func installCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
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(&opts.CleanPatchFiles, cleanPatchFilesFlagName, opts.CleanPatchFiles, cleanPatchFilesFlagUsage)
|
||||
f.BoolVarP(&opts.Pull, pullFlagName, pullFlagShorthand, opts.Pull, pullFlagUsage)
|
||||
f.BoolVarP(&opts.Push, pushFlagName, pushFlagShorthand, opts.Push, pushFlagUsage)
|
||||
|
||||
31
cmd/qliksense/keys.go
Normal file
31
cmd/qliksense/keys.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var keysCmd = &cobra.Command{
|
||||
Use: "keys",
|
||||
Short: "keys for qliksense",
|
||||
}
|
||||
|
||||
func keysRotateCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "rotate",
|
||||
Short: "Rotate qliksense application keys",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := q.InstallQK8s("", &qliksense.InstallCommandOptions{
|
||||
CleanPatchFiles: true,
|
||||
RotateKeys: true,
|
||||
}); err != nil {
|
||||
return err
|
||||
} else {
|
||||
postFlightChecksCmd := AllPostflightChecks(q)
|
||||
postFlightChecksCmd.DisableFlagParsing = true
|
||||
return postFlightChecksCmd.Execute()
|
||||
}
|
||||
},
|
||||
}
|
||||
return c
|
||||
}
|
||||
@@ -24,7 +24,7 @@ func postflightCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
return postflightCmd
|
||||
}
|
||||
|
||||
func pfMigrationCheck(q *qliksense.Qliksense) *cobra.Command {
|
||||
func postflightMigrationCheck(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
postflightOpts := &postflight.PostflightOptions{}
|
||||
var postflightMigrationCmd = &cobra.Command{
|
||||
@@ -58,3 +58,39 @@ func pfMigrationCheck(q *qliksense.Qliksense) *cobra.Command {
|
||||
f.BoolVarP(&postflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return postflightMigrationCmd
|
||||
}
|
||||
|
||||
func AllPostflightChecks(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
postflightOpts := &postflight.PostflightOptions{}
|
||||
var postflightAllChecksCmd = &cobra.Command{
|
||||
Use: "all",
|
||||
Short: "perform all checks",
|
||||
Long: `perform all postflight checks`,
|
||||
Example: `qliksense postflight all`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
pf := &postflight.QliksensePostflight{Q: q, P: postflightOpts, CG: &api.ClientGoUtils{Verbose: postflightOpts.Verbose}}
|
||||
|
||||
// run all postflight checks
|
||||
fmt.Printf("Running all postflight checks...\n\n")
|
||||
namespace, kubeConfigContents, err := pf.CG.LoadKubeConfigAndNamespace()
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "%s\n", Red("Unable to run all postflight checks"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
if err = pf.RunAllPostflightChecks(namespace, kubeConfigContents, postflightOpts); err != nil {
|
||||
fmt.Fprintf(out, "%s\n", Red("1 or more preflight checks have FAILED"))
|
||||
fmt.Printf("Completed running all postflight checks")
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", Green("All postflight checks have PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := postflightAllChecksCmd.Flags()
|
||||
f.BoolVarP(&postflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return postflightAllChecksCmd
|
||||
}
|
||||
|
||||
@@ -472,3 +472,42 @@ func pfCleanupCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return pfCleanCmd
|
||||
}
|
||||
|
||||
func pfVerifyCAChainCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
|
||||
var pfVerifyCAChainCmd = &cobra.Command{
|
||||
Use: "verify-ca-chain",
|
||||
Short: "verify-ca-chain using openssl verify",
|
||||
Long: `verify the CA chain using openssl verify to ensure that mongodb certificate is valid`,
|
||||
Example: `qliksense preflight verify-ca-chain`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||
|
||||
// Preflight service check
|
||||
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
if err = qp.VerifyCAChain(kubeConfigContents, namespace, preflightOpts, false); err != nil {
|
||||
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := pfVerifyCAChainCmd.Flags()
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return pfVerifyCAChainCmd
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ const (
|
||||
cleanPatchFilesFlagUsage = "Set --clean=false to keep any prior config repo file changes on install (for debugging)"
|
||||
pullFlagName = "pull"
|
||||
pullFlagShorthand = "d"
|
||||
pullFlagUsage = "If using private docker registry, pull (download) all required Qliksense images before install"
|
||||
pullFlagUsage = "If using private docker registry, pull (download) all required qliksense images before install"
|
||||
pushFlagName = "push"
|
||||
pushFlagShorthand = "u"
|
||||
pushFlagUsage = "If using private docker registry, push (upload) all downloaded Qliksense images to that registry before install"
|
||||
pushFlagUsage = "If using private docker registry, push (upload) all downloaded qliksense images to that registry before install"
|
||||
rootCommandName = "qliksense"
|
||||
)
|
||||
|
||||
@@ -100,7 +100,7 @@ func commandUsesContext(commandName string) bool {
|
||||
func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: rootCommandName,
|
||||
Short: "Qliksense cli tool",
|
||||
Short: "qliksense cli tool",
|
||||
Long: `qliksense cli tool provides functionality to perform operations on qliksense-k8s, qliksense operator, and kubernetes cluster`,
|
||||
Args: cobra.ArbitraryArgs,
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
@@ -209,6 +209,7 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
preflightCmd.AddCommand(pfCreateRoleBindingCheckCmd(p))
|
||||
preflightCmd.AddCommand(pfCreateServiceAccountCheckCmd(p))
|
||||
preflightCmd.AddCommand(pfCreateAuthCheckCmd(p))
|
||||
preflightCmd.AddCommand(pfVerifyCAChainCmd(p))
|
||||
preflightCmd.AddCommand(pfCleanupCmd(p))
|
||||
|
||||
cmd.AddCommand(preflightCmd)
|
||||
@@ -217,9 +218,14 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
|
||||
// add postflight command
|
||||
postflightCmd := postflightCmd(p)
|
||||
postflightCmd.AddCommand(pfMigrationCheck(p))
|
||||
postflightCmd.AddCommand(postflightMigrationCheck(p))
|
||||
postflightCmd.AddCommand(AllPostflightChecks(p))
|
||||
|
||||
cmd.AddCommand(postflightCmd)
|
||||
|
||||
// add keys command
|
||||
cmd.AddCommand(keysCmd)
|
||||
keysCmd.AddCommand(keysRotateCmd(p))
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,6 @@ spec:
|
||||
- name: mongodbUri
|
||||
value: mongodb://qlik-test-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
```
|
||||
|
||||
`qliksense apply` does everything `qliksense load` does but will install Qlik Sense into the cluster as well
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# How CLI works
|
||||
|
||||
At the initialization, `qliksense` cli creates few files in the director `~/.qliksene` and it contains following files:
|
||||
At the initialization, `qliksense` cli creates few files in the director `~/.qliksense` and it contains following files:
|
||||
|
||||
```console
|
||||
.qliksense
|
||||
@@ -25,7 +25,6 @@ spec:
|
||||
qliksense:
|
||||
- name: mongodbUri
|
||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik-default
|
||||
```
|
||||
|
||||
|
||||
@@ -1,53 +1,162 @@
|
||||
# Getting started
|
||||
|
||||
To get familiar with the Qlik Sense on Kubernetes Operator Command Line Interface (CLI), we will install Qlik Sense on Kubernetes on docker desktop. In subsequent sections we will enhance this configuration to include an Identity Provider (keycloak) and demonstrate air gapped capabilities as well.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Kubernetes cluster (Docker Desktop with enabled Kubernetes)
|
||||
- `kubectl` installed, configured and able to communicate with kubernetes cluster. _`qliksense` CLI uses `kubectl` under the hood to perform operations on cluster_
|
||||
- `kubectl` installed, configured and able to communicate with kubernetes cluster. _`qliksense` CLI uses `kubectl` to perform some operations on cluster_
|
||||
|
||||
## Installing `qliksense` CLI
|
||||
|
||||
Download the executable for your platform from [releases page](https://github.com/qlik-oss/sense-installer/releases) and rename it to `qliksense`
|
||||
|
||||
??? tldr "Linux"
|
||||
=== "Linux"
|
||||
|
||||
``` bash
|
||||
curl -Lo qliksense https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-linux-amd64
|
||||
chmod +x qliksense
|
||||
sudo mv qliksense /usr/local/bin
|
||||
# bash
|
||||
|
||||
curl -LOJ https://storage.googleapis.com/kubernetes-release/release/v1.16.8/bin/linux/amd64/kubectl
|
||||
curl -LOJ https://github.com/qlik-oss/sense-installer/releases/latest/download/qliksense-linux-amd64
|
||||
sudo mv qliksense-linux-amd64 kubectl /usr/local/bin
|
||||
sudo chmod ugo+x /usr/local/bin/qliksense-linux-amd64 /usr/local/bin/kubectl
|
||||
sudo ln -s /usr/local/bin/qliksense-linux-amd64 /usr/local/bin/qliksense
|
||||
sudo ln -s /usr/local/bin/qliksense-linux-amd64 /usr/local/bin/kubectl-qliksense
|
||||
```
|
||||
|
||||
??? tldr "MacOS"
|
||||
=== "MacOS"
|
||||
|
||||
``` bash
|
||||
curl -Lo qliksense https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-darwin-amd64
|
||||
chmod +x qliksense
|
||||
sudo mv qliksense /usr/local/bin
|
||||
# bash
|
||||
|
||||
curl -LOJ https://storage.googleapis.com/kubernetes-release/release/v1.16.8/bin/darwin/amd64/kubectl
|
||||
curl -LOJ https://github.com/qlik-oss/sense-installer/releases/latest/download/qliksense-darwin-amd64
|
||||
sudo mv qliksense-darwin-amd64 kubectl /usr/local/bin
|
||||
sudo chmod ugo+x /usr/local/bin/qliksense-darwin-amd64 /usr/local/bin/kubectl
|
||||
sudo ln -s /usr/local/bin/qliksense-darwin-amd64 /usr/local/bin/qliksense
|
||||
sudo ln -s /usr/local/bin/qliksense-darwin-amd64 /usr/local/bin/kubectl-qliksense
|
||||
```
|
||||
|
||||
??? tldr "Windows"
|
||||
Download Windows executable and add it in your `PATH` as `qliksense.exe`
|
||||
=== "Windows"
|
||||
|
||||
[https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-windows-amd64.exe](https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-windows-amd64.exe)
|
||||
|
||||
``` powershell
|
||||
# powershell
|
||||
|
||||
Invoke-WebRequest https://storage.googleapis.com/kubernetes-release/release/v1.16.8/bin/windows/amd64/kubectl.exe -O C:\bin\kubectl.exe
|
||||
Invoke-WebRequest https://github.com/qlik-oss/sense-installer/releases/latest/download/qliksense-windows-amd64.exe -O C:\bin\qliksense.exe
|
||||
Copy-Item C:\bin\qliksense.exe C:\bin\kubectl-qliksense.exe
|
||||
# Add C:\bin to current Path
|
||||
$Env:Path += ";C:\bin"
|
||||
# Save Path to User environment scope
|
||||
[Environment]::SetEnvironmentVariable("Path",[Environment]::GetEnvironmentVariable("Path", [EnvironmentVariableTarget]::User) + ";C:\bin",[EnvironmentVariableTarget]::User)
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
- To download the version `v0.0.2` from qliksense-k8s [releases](https://github.com/qlik-oss/qliksense-k8s/releases).
|
||||
### Setting the contexts
|
||||
|
||||
By default a `qlik-default` configuration context is provided and can be used, as is. In effect, this is the name of the Qlik Sense instance in the target cluster. All resources installed into the target namespace will be prefixed with `qlik-default`. The name of the Qlik Sense application will correspondingly be `qliksense`.
|
||||
|
||||
Ex.: To change this to `qliksense-dev`:
|
||||
|
||||
```shell
|
||||
qliksense fetch v0.0.2
|
||||
qliksense config set-context qliksense-dev
|
||||
```
|
||||
|
||||
- To install CRDs for QSEoK and qliksense operator into the kubernetes cluster.
|
||||
!!! info ""
|
||||
For the purposes of the Quick Start we will be using `qlik-default`
|
||||
|
||||
The target namespace is determined by the kubectl connection context.
|
||||
|
||||
ex. Ensure a connection to cluster to change the configuration context's target namespace with kubectl to `qliksense`
|
||||
|
||||
```shell
|
||||
qliksense crds install --all
|
||||
kubectl config set-context --current --namespace=qliksense
|
||||
```
|
||||
|
||||
- To install QSEoK into a namespace in the kubernetes cluster where `kubectl` is pointing to.
|
||||
!!! info ""
|
||||
For the purposes of the Quick Start we will be using the default namespace. (`default`)
|
||||
|
||||
### Downloading a version of Qlik Sense on Kubernetes
|
||||
|
||||
To download the latest version of Qlik Sense on Kubernetes from qliksense-k8s
|
||||
|
||||
```shell
|
||||
qliksense install --acceptEULA="yes"
|
||||
qliksense fetch
|
||||
```
|
||||
|
||||
#### More Options
|
||||
|
||||
- To download a specific version `v1.59.20` from qliksense-k8s [releases](https://github.com/qlik-oss/qliksense-k8s/releases)
|
||||
```shell
|
||||
qliksense fetch v1.58.20
|
||||
```
|
||||
- To download from a GitHub repository fork of the `qliksense-k8s` repository (master branch)
|
||||
```shell
|
||||
qliksense fetch --url https://github.com/bkuschel/qliksense-k8s.git master
|
||||
```
|
||||
|
||||
### Deployment Profiles
|
||||
|
||||
Deployment profiles are a sets components that require sets of key/value pairs to satisfy the requirements for the generation of a Qlik Sense on Kubernetes manifest. Along with the profile name, sets of key/value pairs are provided through the Qlik Sense custom application resources (see here).
|
||||
|
||||
Profiles can be developed and added to the qliksense-k8s repo but is considered an advanced topic (see here) not covered here.
|
||||
|
||||
#### Default Profile: Docker Desktop
|
||||
|
||||
By default, the `docker-desktop` profile is associated with the configuration context when initially created. This profile is guaranteed to work on Docker Desktop but can generally be used on other types of Kubernetes clusters, provided that the required configuration tweaks are provided specific to the hosting requirements (Ex. storage class).
|
||||
|
||||
The docker-desktop profile does not have any scaling characteristics and is generally set up to have the ability to work on a reasonably powerful computer (16GB, 4 cores minimum, greater is better). It also includes a self-contained mongodb instance for non-production purposes.
|
||||
|
||||
Generally it doesn't require any extra configuration to work except an acceptance of the Qlik User License Agreement (QULA), which is prompted on install but can also be set in advance (having read the QULA)
|
||||
|
||||
```shell
|
||||
qliksense config set-configs qliksense.acceptEULA="yes"
|
||||
```
|
||||
|
||||
More information on the possible configuration parameters for docker-desktop here (see here).
|
||||
|
||||
!!! Info
|
||||
To access an installation of the docker desktop profile in docker desktop, the host `elastic.example` needs to be added to the system host file as an alias to `127.0.0.1`
|
||||
|
||||
```
|
||||
127.0.0.1 elastic.example
|
||||
```
|
||||
|
||||
File location:
|
||||
|
||||
- Linux - `/etc/hosts`
|
||||
- MacOS - `/etc/hosts`
|
||||
- Windows - `C:\Windows\System32\drivers\etc\hosts`
|
||||
|
||||
### Installing Qlik Sense on Kubernetes
|
||||
|
||||
#### Custom Resource Definitions (CRDs)
|
||||
|
||||
Besides the CLI, a Kubernetes operator (read here) is a core component of the Qlik Sense Operator. Additionally, there are other Kubernetes operators in Qlik Sense on Kubernetes that provide other types functionality (ex. scaling). Depending on the profile chosen [(see Deployment profiles)](#deployment-profiles), additional CRDs can also be installed for third-party components (see gke-demo).
|
||||
|
||||
Kubernetes operators require Custom resource definitions (CRD) (read here), which are YAML schemas for custom resources (CR). The Qlik Sense application instance, corresponding to the name of the configuration context, corresponds to a CR (ex. `qlik-default`).
|
||||
|
||||
CRDs require cluster scope permissions and are shared cluster-wide across namespaces. These need to be installed first (if not done previously).
|
||||
|
||||
To install CRDs for Qlik Sense on Kubernetes into the Kubernetes cluster.
|
||||
|
||||
```shell
|
||||
qliksense crds install
|
||||
```
|
||||
|
||||
#### Preflight Checks
|
||||
|
||||
To check that your environment fullfills Qlik Sense requirements
|
||||
|
||||
```shell
|
||||
qliksense preflight all
|
||||
```
|
||||
|
||||
#### Qlik Sense
|
||||
|
||||
To install Qlik Sense into a namespace in the Kubernetes cluster where `kubectl` is pointing to
|
||||
|
||||
```shell
|
||||
qliksense install
|
||||
```
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
# Overview
|
||||
|
||||
The Qlik Sense on Kubernetes CLI (`qliksense`) provides an imperative interface to many of the configurations that need to be applied against the declarative structure described in [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s).
|
||||
|
||||
The CLI facilitates:
|
||||
The Qlik Sense on Kubernetes Operator CLI (`qliksense`) facilitates:
|
||||
|
||||
- Installation of QSEoK
|
||||
- Installation of qliksense operator to manage QSEoK
|
||||
- Installation of Qliksense operator to manage the QSEoK installation
|
||||
- Air gapped installation of QSEoK
|
||||
- Cluster configuration management
|
||||
- Pre-flight and Post-flight environment and configuration checks
|
||||
|
||||
The Qlik Sense on Kubernetes Operator CLI provides an imperative interface to many of the configurations that need to be applied against the declarative structure described in the [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s) repository
|
||||
|
||||
To get start quickly go to the [Getting Started page](getting_started.md).
|
||||
|
||||
To learn more about the internal workings of the Qlik Sense on Kubernetes Operator, go to [How CLI works](concepts.md).
|
||||
|
||||
!!! info ""
|
||||
This is a technology preview that uses Qlik modified [kustomize](https://github.com/qlik-oss/kustomize) for Kubernetes manifests on [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s) repository
|
||||
|
||||
|
||||
!!! info ""
|
||||
See QlikSense [edge releases on qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s/releases) repository
|
||||
|
||||
@@ -20,6 +20,22 @@ Flags:
|
||||
-v, --verbose verbose mode
|
||||
```
|
||||
|
||||
### Run all postflight checks
|
||||
This command runs all the postflight checks available.
|
||||
|
||||
```shell
|
||||
$ qliksense postflight all
|
||||
Running all postflight checks...
|
||||
|
||||
Postflight db migration check...
|
||||
Logs from pod: qliksense-users-6977cb7788-qlgmv
|
||||
{"caller":"main.go:39","environment":"qseok","error":"error parsing uri: scheme must be \"mongodb\" or \"mongodb+srv\"","level":"error","message":"failed to connect to ","timestamp":"2020-06-17T04:10:11.7891913Z","version":""}
|
||||
To view more logs in this context, please run the command: kubectl logs -n test_ns qliksense-users-6977cb7788-qlgmv migration
|
||||
PASSED
|
||||
|
||||
All postflight checks have PASSED
|
||||
```
|
||||
|
||||
### DB migration check
|
||||
This command checks init containers for successful database migrarion completions, and reports failure, if any to the user.
|
||||
|
||||
@@ -29,5 +45,7 @@ An example run of this check produces an output as shown below:
|
||||
$ qliksense postflight db-migration-check
|
||||
Logs from pod: qliksense-users-6977cb7788-cxxwh
|
||||
{"caller":"main.go:39","environment":"qseok","error":"error parsing uri: scheme must be \"mongodb\" or \"mongodb+srv\"","level":"error","message":"failed to connect to ","timestamp":"2020-06-01T01:07:18.4170507Z","version":""}
|
||||
To view more logs in this context, please run the command: kubectl logs -n test_ns qliksense-users-6977cb7788-qlgmv migration
|
||||
PASSED
|
||||
Postflight db_migration_check completed
|
||||
```
|
||||
@@ -305,3 +305,21 @@ Removing mongo check components...
|
||||
Preflight cleanup complete
|
||||
|
||||
```
|
||||
|
||||
### Verify-ca-chain check
|
||||
We use the command below to verify the ca certificate chain and server certificate. We run this check over mongodbUrl and discoveryUrl we inferred from idpconfigs in the CR.
|
||||
```shell
|
||||
$ qliksense preflight preflight verify-ca-chain -v
|
||||
|
||||
Preflight verify-ca-chain check...
|
||||
-----------------------------------
|
||||
Openssl verify mongodbUrl:
|
||||
Mongodb url inferred form CR: <mongodbUrl_from_CR>
|
||||
Host: <host extracted from mongodbUrl>
|
||||
|
||||
Openssl verify discoveryUrl:
|
||||
Discovery url: <discoveryUrl_from_CR>
|
||||
Host: <host extracted from discoveryUrl>
|
||||
Completed preflight verify-CA-chain check
|
||||
PASSED
|
||||
```
|
||||
7
go.mod
7
go.mod
@@ -10,7 +10,7 @@ replace (
|
||||
k8s.io/client-go => k8s.io/client-go v0.17.0
|
||||
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
|
||||
|
||||
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200604192606-17370c1af57b
|
||||
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -22,7 +22,7 @@ require (
|
||||
github.com/bugsnag/bugsnag-go v1.5.3 // indirect
|
||||
github.com/containers/image/v5 v5.1.0
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/go-git/go-git/v5 v5.0.0
|
||||
github.com/go-git/go-git/v5 v5.4.2
|
||||
github.com/gobuffalo/envy v1.9.0 // indirect
|
||||
github.com/gobuffalo/logger v1.0.3 // indirect
|
||||
github.com/gobuffalo/packd v1.0.0 // indirect
|
||||
@@ -40,7 +40,7 @@ require (
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/otiai10/copy v1.1.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/qlik-oss/k-apis v0.1.7
|
||||
github.com/qlik-oss/k-apis v0.1.16
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
||||
github.com/spf13/cobra v0.0.6
|
||||
@@ -55,6 +55,7 @@ require (
|
||||
k8s.io/apiextensions-apiserver v0.17.2
|
||||
k8s.io/apimachinery v0.17.2
|
||||
k8s.io/client-go v11.0.0+incompatible
|
||||
k8s.io/kubectl v0.17.2
|
||||
sigs.k8s.io/kustomize/api v0.3.2
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
||||
24
go.sum
24
go.sum
@@ -309,6 +309,8 @@ github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
|
||||
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
|
||||
github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk=
|
||||
github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
@@ -471,10 +473,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-replayers/grpcreplay v0.1.0 h1:eNb1y9rZFmY4ax45uEEECSa8fsxGRU+8Bil52ASAwic=
|
||||
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
|
||||
github.com/google/go-replayers/httpreplay v0.1.0 h1:AX7FUb4BjrrzNvblr/OlgwrmFiep6soj5K2QSDW7BGk=
|
||||
@@ -624,6 +622,8 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
|
||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
@@ -885,16 +885,10 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
|
||||
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
|
||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/qlik-oss/k-apis v0.1.5 h1:IeqHuF1IIQCsuSmsUhL7GjdfkOFsNgh3z2UyX59GTsk=
|
||||
github.com/qlik-oss/k-apis v0.1.5/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
|
||||
github.com/qlik-oss/k-apis v0.1.6 h1:4rTRwgIN1GtkmLa0VBjvpW2oyVC3h1fOcFHEg3NieNA=
|
||||
github.com/qlik-oss/k-apis v0.1.6/go.mod h1:r5hXo1mrHOzIdI0Ri9TI4SKjEXft1TZnAyJzOSm9pi0=
|
||||
github.com/qlik-oss/k-apis v0.1.7 h1:3QPymn+xMhwslm1F0oqdVqtJ/FdfAJn4vNnmS9NFIoY=
|
||||
github.com/qlik-oss/k-apis v0.1.7/go.mod h1:r5hXo1mrHOzIdI0Ri9TI4SKjEXft1TZnAyJzOSm9pi0=
|
||||
github.com/qlik-oss/k-apis v0.7.1 h1:7XHTcIVTSgBM8s9LglWbOeWlnKm84cgHIm7Slrjxb9Y=
|
||||
github.com/qlik-oss/k-apis v0.7.1/go.mod h1:r5hXo1mrHOzIdI0Ri9TI4SKjEXft1TZnAyJzOSm9pi0=
|
||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200604192606-17370c1af57b h1:UqyMR9WA/MbUxCOaaDFk8D2VwM3fG98foDU5nG2HPoM=
|
||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200604192606-17370c1af57b/go.mod h1:zh3yFgE5zFk1kreqzVyyj1eXyIxQJT53l4zSg8Wt4SA=
|
||||
github.com/qlik-oss/k-apis v0.1.16 h1:R3gCZs4A3EHPNx4B7p1idWD+OhyaU/bAlGYBWc0ZNz4=
|
||||
github.com/qlik-oss/k-apis v0.1.16/go.mod h1:AkNa/kaZHpGVs9l+pHe6nvz99Sp9WO1f9ylBES95o+I=
|
||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b h1:RDh3OZJOriy/ap1NUHVKsPG07N4DALaCzaqXFFK57T0=
|
||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b/go.mod h1:zh3yFgE5zFk1kreqzVyyj1eXyIxQJT53l4zSg8Wt4SA=
|
||||
github.com/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=
|
||||
@@ -1172,8 +1166,6 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8ou
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
site_name: Qlik Sense on Kubernetes CLI
|
||||
repo_url: 'https://github.com/qlik-oss/sense-installer'
|
||||
strict: true
|
||||
|
||||
theme:
|
||||
name: "material"
|
||||
palette:
|
||||
primary: 'green'
|
||||
accent: 'indigo'
|
||||
|
||||
markdown_extensions:
|
||||
- toc:
|
||||
permalink: true
|
||||
@@ -14,6 +16,8 @@ markdown_extensions:
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.superfences
|
||||
- pymdownx.details
|
||||
- pymdownx.tabbed
|
||||
|
||||
nav:
|
||||
- Overview: index.md
|
||||
- getting_started.md
|
||||
|
||||
@@ -144,17 +144,17 @@ func (cr *QliksenseCR) IsRepoExist() bool {
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) GetFetchUrl() string {
|
||||
if cr.Spec.FetchSource == nil || cr.Spec.FetchSource.Repository == "" {
|
||||
if cr.Spec.Git == nil || cr.Spec.Git.Repository == "" {
|
||||
return QLIK_GIT_REPO
|
||||
}
|
||||
return cr.Spec.FetchSource.Repository
|
||||
return cr.Spec.Git.Repository
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) GetFetchAccessToken(encryptionKey string) string {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
if cr.Spec.Git == nil {
|
||||
return ""
|
||||
}
|
||||
if tok, err := cr.Spec.FetchSource.GetAccessToken(); err != nil {
|
||||
if tok, err := cr.Spec.Git.GetAccessToken(); err != nil {
|
||||
fmt.Println(err)
|
||||
return ""
|
||||
} else if tok == "" {
|
||||
@@ -171,29 +171,29 @@ func (cr *QliksenseCR) GetFetchAccessToken(encryptionKey string) string {
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) SetFetchUrl(url string) {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
cr.Spec.FetchSource = &config.Repo{}
|
||||
if cr.Spec.Git == nil {
|
||||
cr.Spec.Git = &config.Repo{}
|
||||
}
|
||||
cr.Spec.FetchSource.Repository = url
|
||||
cr.Spec.Git.Repository = url
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) SetFetchAccessToken(token, encryptionKey string) error {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
cr.Spec.FetchSource = &config.Repo{}
|
||||
if cr.Spec.Git == nil {
|
||||
cr.Spec.Git = &config.Repo{}
|
||||
}
|
||||
res, err := EncryptData([]byte(token), encryptionKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cr.Spec.FetchSource.AccessToken = b64.StdEncoding.EncodeToString(res)
|
||||
cr.Spec.Git.AccessToken = b64.StdEncoding.EncodeToString(res)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) SetFetchAccessSecretName(sec string) {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
cr.Spec.FetchSource = &config.Repo{}
|
||||
if cr.Spec.Git == nil {
|
||||
cr.Spec.Git = &config.Repo{}
|
||||
}
|
||||
cr.Spec.FetchSource.SecretName = sec
|
||||
cr.Spec.Git.SecretName = sec
|
||||
}
|
||||
|
||||
//DeleteRepo delete the manifest repo and unset manifestsRoot
|
||||
@@ -524,9 +524,9 @@ func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error)
|
||||
}
|
||||
newCr.Spec.Secrets = finalSecrets
|
||||
|
||||
if newCr.Spec.FetchSource != nil && newCr.Spec.FetchSource.AccessToken != "" {
|
||||
if newCr.Spec.Git != nil && newCr.Spec.Git.AccessToken != "" {
|
||||
decData := cr.GetFetchAccessToken(encryptionKey)
|
||||
newCr.Spec.FetchSource.AccessToken = decData
|
||||
newCr.Spec.Git.AccessToken = decData
|
||||
}
|
||||
return newCr, nil
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ func TestGetDecryptedCr(t *testing.T) {
|
||||
if decryptedValue == orignalValue {
|
||||
t.Fail()
|
||||
}
|
||||
if newCr.Spec.FetchSource.AccessToken != "mytoken" {
|
||||
if newCr.Spec.Git.AccessToken != "mytoken" {
|
||||
t.Fail()
|
||||
}
|
||||
td()
|
||||
|
||||
@@ -508,7 +508,7 @@ func TestClientGoUtils_CreatePreflightTestPod(t *testing.T) {
|
||||
ImagePullPolicy: apiv1.PullIfNotPresent,
|
||||
Command: []string{"echo"},
|
||||
VolumeMounts: []apiv1.VolumeMount{
|
||||
apiv1.VolumeMount{
|
||||
{
|
||||
Name: "secret1",
|
||||
MountPath: filepath.Dir("/etc/secret1"),
|
||||
ReadOnly: true,
|
||||
@@ -517,7 +517,7 @@ func TestClientGoUtils_CreatePreflightTestPod(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Volumes: []apiv1.Volume{
|
||||
apiv1.Volume{
|
||||
{
|
||||
Name: "secret1",
|
||||
VolumeSource: apiv1.VolumeSource{
|
||||
Secret: &apiv1.SecretVolumeSource{
|
||||
@@ -844,7 +844,7 @@ func TestClientGoUtils_WaitForPod(t *testing.T) {
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
apiv1.Container{},
|
||||
{},
|
||||
},
|
||||
},
|
||||
Status: apiv1.PodStatus{
|
||||
@@ -895,7 +895,7 @@ func TestClientGoUtils_WaitForPod(t *testing.T) {
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
apiv1.Container{},
|
||||
{},
|
||||
},
|
||||
},
|
||||
Status: apiv1.PodStatus{
|
||||
@@ -942,7 +942,7 @@ func TestClientGoUtils_WaitForPod(t *testing.T) {
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
apiv1.Container{},
|
||||
{},
|
||||
},
|
||||
},
|
||||
Status: apiv1.PodStatus{
|
||||
@@ -1059,7 +1059,7 @@ func TestClientGoUtils_WaitForPodToDie(t *testing.T) {
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
apiv1.Container{},
|
||||
{},
|
||||
},
|
||||
},
|
||||
Status: apiv1.PodStatus{
|
||||
@@ -1131,7 +1131,7 @@ func TestClientGoUtils_WaitForPodToDie(t *testing.T) {
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
apiv1.Container{},
|
||||
{},
|
||||
},
|
||||
},
|
||||
Status: apiv1.PodStatus{
|
||||
@@ -1885,10 +1885,10 @@ func TestClientGoUtils_waitForStatefulsetToDelete(t *testing.T) {
|
||||
statefulsetName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
timeoutForChangingReplicaCount time.Duration
|
||||
}{
|
||||
{
|
||||
@@ -1897,8 +1897,8 @@ func TestClientGoUtils_waitForStatefulsetToDelete(t *testing.T) {
|
||||
Verbose: true,
|
||||
},
|
||||
args: args{
|
||||
clientset: fake.NewSimpleClientset(ss),
|
||||
namespace: "test-ns",
|
||||
clientset: fake.NewSimpleClientset(ss),
|
||||
namespace: "test-ns",
|
||||
statefulsetName: ss.Name,
|
||||
},
|
||||
wantErr: false,
|
||||
@@ -1910,11 +1910,11 @@ func TestClientGoUtils_waitForStatefulsetToDelete(t *testing.T) {
|
||||
Verbose: true,
|
||||
},
|
||||
args: args{
|
||||
clientset: fake.NewSimpleClientset(),
|
||||
namespace: "test-ns",
|
||||
clientset: fake.NewSimpleClientset(),
|
||||
namespace: "test-ns",
|
||||
statefulsetName: ss.Name,
|
||||
},
|
||||
wantErr: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "timeout",
|
||||
@@ -1922,11 +1922,11 @@ func TestClientGoUtils_waitForStatefulsetToDelete(t *testing.T) {
|
||||
Verbose: true,
|
||||
},
|
||||
args: args{
|
||||
clientset: fake.NewSimpleClientset(ss),
|
||||
namespace: "test-ns",
|
||||
clientset: fake.NewSimpleClientset(ss),
|
||||
namespace: "test-ns",
|
||||
statefulsetName: ss.Name,
|
||||
},
|
||||
wantErr: true,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -23,7 +23,6 @@ const (
|
||||
QliksenseKind = "Qliksense"
|
||||
QliksenseGroup = "qlik.com"
|
||||
QliksenseDefaultProfile = "docker-desktop"
|
||||
DefaultRotateKeys = "yes"
|
||||
QliksenseMetadataName = "QliksenseConfigMetadata"
|
||||
DefaultMongodbUri = "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"
|
||||
DefaultMongodbUriKey = "mongodbUri"
|
||||
@@ -38,8 +37,7 @@ func (qliksenseCR *QliksenseCR) AddCommonConfig(contextName string) {
|
||||
})
|
||||
qliksenseCR.SetName(contextName)
|
||||
qliksenseCR.Spec = &config.CRSpec{
|
||||
Profile: QliksenseDefaultProfile,
|
||||
RotateKeys: DefaultRotateKeys,
|
||||
Profile: QliksenseDefaultProfile,
|
||||
}
|
||||
qliksenseCR.Spec.AddToSecrets("qliksense", DefaultMongodbUriKey, strings.Replace(DefaultMongodbUri, "qlik-default", contextName, 1), "")
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ func TestAddCommonConfig(t *testing.T) {
|
||||
q.SetName("myqliksense")
|
||||
q.SetGroupVersionKind(gvk)
|
||||
q.Spec = &config.CRSpec{
|
||||
Profile: QliksenseDefaultProfile,
|
||||
RotateKeys: DefaultRotateKeys,
|
||||
Profile: QliksenseDefaultProfile,
|
||||
Secrets: map[string]config.NameValues{
|
||||
"qliksense": []config.NameValue{{
|
||||
Name: DefaultMongodbUriKey,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
|
||||
@@ -60,7 +61,7 @@ func TestCopyDirectory_withGit_withKuz(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := kapis_git.Checkout(repo2, "v0.0.2", "", nil); err != nil {
|
||||
if err := kapis_git.Checkout(repo2, "v0.0.8", "", nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
@@ -69,7 +70,7 @@ func TestCopyDirectory_withGit_withKuz(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := kapis_git.Checkout(repo1, "v0.0.2", "", nil); err != nil {
|
||||
if err := kapis_git.Checkout(repo1, "v0.0.8", "", nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
@@ -78,9 +79,15 @@ func TestCopyDirectory_withGit_withKuz(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if string(repo2Manifest) != string(repo1Manifest) {
|
||||
t.Logf("manifest generated on the original config:\n%v", string(repo1Manifest))
|
||||
t.Logf("manifest generated on the copied config:\n%v", string(repo2Manifest))
|
||||
re, err := regexp.Compile(`name: qliksense-ca-certificates-[a-z]{5}`)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
repo1ManifestTweaked := re.ReplaceAllString(string(repo1Manifest), "name: qliksense-ca-certificates")
|
||||
repo2ManifestTweaked := re.ReplaceAllString(string(repo2Manifest), "name: qliksense-ca-certificates")
|
||||
if repo2ManifestTweaked != repo1ManifestTweaked {
|
||||
t.Logf("manifest generated on the original config:\n%v", repo1ManifestTweaked)
|
||||
t.Logf("manifest generated on the copied config:\n%v", repo2ManifestTweaked)
|
||||
t.Fatal("expected manifests to be equal, but they were not")
|
||||
}
|
||||
}
|
||||
|
||||
31
pkg/postflight/all_postflight_checks.go
Normal file
31
pkg/postflight/all_postflight_checks.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package postflight
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "github.com/logrusorgru/aurora"
|
||||
ansi "github.com/mattn/go-colorable"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (qp *QliksensePostflight) RunAllPostflightChecks(namespace string, kubeConfigContents []byte, preflightOpts *PostflightOptions) error {
|
||||
checkCount := 0
|
||||
totalCount := 0
|
||||
|
||||
out := ansi.NewColorableStdout()
|
||||
// Postflight db migration check
|
||||
if err := qp.DbMigrationCheck(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||
checkCount++
|
||||
}
|
||||
totalCount++
|
||||
|
||||
if checkCount == totalCount {
|
||||
// All postflight checks were successful
|
||||
return nil
|
||||
}
|
||||
return errors.New("1 or more postflight checks have FAILED")
|
||||
}
|
||||
@@ -11,7 +11,8 @@ import (
|
||||
const initContainerNameToCheck = "migration"
|
||||
|
||||
func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigContents []byte) error {
|
||||
|
||||
fmt.Printf("Postflight db migration check... \n")
|
||||
p.CG.LogVerboseMessage("\n----------------------------------- \n")
|
||||
clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create a kubernetes client: %v", err)
|
||||
|
||||
@@ -103,6 +103,16 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
|
||||
}
|
||||
totalCount++
|
||||
|
||||
// Preflight verify ca chain check
|
||||
if err := qp.VerifyCAChain(kubeConfigContents, namespace, preflightOpts, false); err != nil {
|
||||
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||
checkCount++
|
||||
}
|
||||
totalCount++
|
||||
|
||||
if checkCount == totalCount {
|
||||
// All preflight checks were successful
|
||||
return nil
|
||||
|
||||
123
pkg/preflight/verify_ca.go
Normal file
123
pkg/preflight/verify_ca.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package preflight
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func (qp *QliksensePreflight) VerifyCAChain(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error {
|
||||
|
||||
var currentCR *qapi.QliksenseCR
|
||||
var err error
|
||||
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
|
||||
qConfig.SetNamespace(namespace)
|
||||
|
||||
fmt.Print("Preflight verify-ca-chain check... ")
|
||||
qp.CG.LogVerboseMessage("\n----------------------------------- \n")
|
||||
|
||||
currentCR, err = qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
qp.CG.LogVerboseMessage("Unable to retrieve current CR: %v\n", err)
|
||||
return err
|
||||
}
|
||||
decryptedCR, err := qConfig.GetDecryptedCr(currentCR)
|
||||
if err != nil {
|
||||
qp.CG.LogVerboseMessage("An error occurred while retrieving mongodbUrl from current CR: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// infer ca certs form CR
|
||||
caCertificates := strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("qliksense", "caCertificates"))
|
||||
|
||||
fmt.Println("Openssl verify mongodbUrl:")
|
||||
// infer mongodb url from CR
|
||||
mongodbUrl := strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("qliksense", "mongodbUri"))
|
||||
qp.CG.LogVerboseMessage("Mongodb url inferred form CR: %s\n", mongodbUrl)
|
||||
|
||||
// parse out server and port from mongodb url and execute openssl verify
|
||||
if err := qp.extractCertAndVerify(mongodbUrl, caCertificates); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("\nOpenssl verify discoveryUrl:\n")
|
||||
// infer idpConfigs form CR
|
||||
idpConfigs := strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("identity-providers", "idpConfigs"))
|
||||
|
||||
data := []map[string]interface{}{}
|
||||
if err := json.Unmarshal([]byte(idpConfigs), &data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var discoveryUrl string
|
||||
for _, idpData := range data {
|
||||
discoveryUrl = idpData["discoveryUrl"].(string)
|
||||
qp.CG.LogVerboseMessage("Discovery url: %s\n", discoveryUrl)
|
||||
}
|
||||
if err := qp.extractCertAndVerify(discoveryUrl, caCertificates); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qp.CG.LogVerboseMessage("Completed preflight verify-ca-chain check\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qp *QliksensePreflight) extractCertAndVerify(server string, caCertificates string) error {
|
||||
u, err := url.Parse(server)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse url: %v", err)
|
||||
}
|
||||
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "http":
|
||||
return fmt.Errorf("http url is not supported for this operation")
|
||||
case "https":
|
||||
if u.Port() == "" {
|
||||
u.Host += ":443"
|
||||
}
|
||||
}
|
||||
|
||||
qp.CG.LogVerboseMessage("Host: %s, port: %s\n", u.Host, u.Port())
|
||||
conn, err := tls.Dial("tcp", u.Host, &tls.Config{})
|
||||
qp.CG.LogVerboseMessage("Host: %s\n", u.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: " + err.Error())
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Get the ConnectionState struct as that's the one which gives us x509.Certificate struct
|
||||
x509Certificates := conn.ConnectionState().PeerCertificates
|
||||
|
||||
var serverCert *x509.Certificate
|
||||
if len(x509Certificates) == 0 {
|
||||
return fmt.Errorf("no server certificates retrieved from the server")
|
||||
}
|
||||
// we retrieve and verify the server certificate, we ignore intermediate certificates at this point.
|
||||
for _, x509Cert := range x509Certificates {
|
||||
if !x509Cert.IsCA {
|
||||
serverCert = x509Cert
|
||||
break
|
||||
}
|
||||
}
|
||||
if serverCert == nil {
|
||||
return fmt.Errorf("no valid server certificates retrieved from the server")
|
||||
}
|
||||
roots := x509.NewCertPool()
|
||||
if ok := roots.AppendCertsFromPEM([]byte(caCertificates)); !ok {
|
||||
return fmt.Errorf("failed to parse root certificate.")
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: roots,
|
||||
DNSName: u.Hostname(),
|
||||
}
|
||||
if _, err := serverCert.Verify(opts); err != nil {
|
||||
return fmt.Errorf("failed to verify certificate: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -5,16 +5,19 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/qlik-oss/k-apis/pkg/config"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/qlik-oss/k-apis/pkg/cr"
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"k8s.io/kubectl/pkg/cmd/util/editor"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -73,20 +76,21 @@ func (q *Qliksense) configEjson() error {
|
||||
}
|
||||
|
||||
func (q *Qliksense) applyConfigToK8s(qcr *qapi.QliksenseCR) error {
|
||||
if qcr.Spec.RotateKeys != "None" {
|
||||
if err := q.configEjson(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := q.configEjson(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userHomeDir, err := homedir.Dir()
|
||||
if err != nil {
|
||||
fmt.Printf(`error fetching user's home directory: %v\n`, err)
|
||||
return err
|
||||
}
|
||||
fmt.Println("Manifests root: " + qcr.Spec.GetManifestsRoot())
|
||||
qcr.SetNamespace(qapi.GetKubectlNamespace())
|
||||
b, _ := yaml.Marshal(qcr.KApiCr)
|
||||
fmt.Printf("%v", string(b))
|
||||
// os.Exit(0)
|
||||
// generate patches
|
||||
cr.GeneratePatches(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config"))
|
||||
cr.GeneratePatches(&qcr.KApiCr, config.KeysActionRestoreOrRotate, path.Join(userHomeDir, ".kube", "config"))
|
||||
// apply generated manifests
|
||||
profilePath := filepath.Join(qcr.Spec.GetManifestsRoot(), qcr.Spec.GetProfileDir())
|
||||
fmt.Printf("Generating manifests for profile: %v\n", profilePath)
|
||||
@@ -192,13 +196,12 @@ func (q *Qliksense) EditCR(contextName string) error {
|
||||
if err := ioutil.WriteFile(tempFile.Name(), crContent, os.ModePerm); err != nil {
|
||||
return nil
|
||||
}
|
||||
cmd := exec.Command(getKubeEditorTool(), tempFile.Name())
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
|
||||
currentEditor := editor.NewDefaultEditor([]string{"KUBE_EDITOR", "EDITOR"})
|
||||
if err = currentEditor.Launch(tempFile.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newCr, err := qapi.GetCRObject(tempFile.Name())
|
||||
if err != nil {
|
||||
return errors.New("cannot save the cr. Someting wrong in the file format. It is not saved\n" + err.Error())
|
||||
@@ -213,11 +216,3 @@ func (q *Qliksense) EditCR(contextName string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getKubeEditorTool() string {
|
||||
editor := os.Getenv("KUBE_EDITOR")
|
||||
if editor == "" {
|
||||
editor = "vim"
|
||||
}
|
||||
return editor
|
||||
}
|
||||
|
||||
@@ -176,53 +176,6 @@ func caseInsenstiveFieldByName(v reflect.Value, name string) reflect.Value {
|
||||
return v.FieldByNameFunc(func(n string) bool { return strings.ToLower(n) == name })
|
||||
}
|
||||
|
||||
func validateCR(key string, keySub string, value string, crSpec *api.QliksenseCR) (bool, *api.QliksenseCR) {
|
||||
cr := reflect.ValueOf(crSpec.Spec)
|
||||
keyValid := caseInsenstiveFieldByName(reflect.Indirect(cr), key)
|
||||
if !keyValid.IsValid() {
|
||||
//not in main spec
|
||||
fmt.Println(key, "is an invalid key")
|
||||
return false, crSpec
|
||||
} else if keySub == "" {
|
||||
if key == "rotatekeys" {
|
||||
if _, err := validateInput(value); err != nil {
|
||||
return false, crSpec
|
||||
}
|
||||
}
|
||||
}
|
||||
// checks if it is git or gitops
|
||||
if keySub != "" {
|
||||
if !keyValid.IsNil() {
|
||||
if !caseInsenstiveFieldByName(reflect.Indirect(keyValid), keySub).IsValid() {
|
||||
fmt.Println(keySub, "is an invalid key")
|
||||
return false, crSpec
|
||||
} else {
|
||||
// verify gitops enabled and gitops schedule
|
||||
switch keySub {
|
||||
case "schedule":
|
||||
if _, err := cron.ParseStandard(value); err != nil {
|
||||
fmt.Println("Please enter string with standard cron scheduling syntax ")
|
||||
return false, crSpec
|
||||
}
|
||||
case "enabled":
|
||||
if !strings.EqualFold(value, "yes") && !strings.EqualFold(value, "no") {
|
||||
fmt.Println("Please use yes or no for key enabled")
|
||||
return false, crSpec
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch key {
|
||||
case "opsrunner":
|
||||
crSpec.Spec.OpsRunner = &config.OpsRunner{}
|
||||
case "git":
|
||||
crSpec.Spec.Git = &config.Repo{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, crSpec
|
||||
}
|
||||
|
||||
// SetOtherConfigs - set profile/storageclassname/git.repository/manifestRoot commands
|
||||
func (q *Qliksense) SetOtherConfigs(args []string) error {
|
||||
// retieve current context from config.yaml
|
||||
@@ -240,11 +193,7 @@ func (q *Qliksense) SetOtherConfigs(args []string) error {
|
||||
}
|
||||
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "fetchSource.") {
|
||||
if err := q.processSetFetchSource(arg, qliksenseCR); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if strings.HasPrefix(arg, "git.") {
|
||||
if strings.HasPrefix(arg, "git.") {
|
||||
if err := q.processSetGit(arg, qliksenseCR); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -273,64 +222,34 @@ func processSetSingleArg(arg string, cr *api.QliksenseCR) error {
|
||||
cr.Spec.Profile = nv[1]
|
||||
case "storageClassName":
|
||||
cr.Spec.StorageClassName = nv[1]
|
||||
case "rotateKeys":
|
||||
valid := false
|
||||
for _, v := range []string{"yes", "no", "None"} {
|
||||
if nv[1] == v {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return errors.New("please povide rotateKeys=yes|no|None")
|
||||
}
|
||||
cr.Spec.RotateKeys = nv[1]
|
||||
default:
|
||||
return errors.New("Please enter one of: profile, storageClassName,rotateKeys, manifestRoot to configure the current context")
|
||||
return errors.New("Please enter one of: profile, storageClassName, manifestRoot, git to configure the current context")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) processSetFetchSource(arg string, cr *api.QliksenseCR) error {
|
||||
args := strings.Split(arg, "=")
|
||||
subs := strings.Split(args[0], ".")
|
||||
if cr.Spec.FetchSource == nil {
|
||||
cr.Spec.FetchSource = &config.Repo{}
|
||||
func (q *Qliksense) processSetGit(arg string, cr *api.QliksenseCR) error {
|
||||
s := strings.Split(arg, "=")
|
||||
tArg0 := strings.TrimSpace(s[0])
|
||||
tArg1 := strings.TrimSpace(s[1])
|
||||
subs := strings.Split(tArg0, ".")
|
||||
if cr.Spec.Git == nil {
|
||||
cr.Spec.Git = &config.Repo{}
|
||||
}
|
||||
switch subs[1] {
|
||||
case "repository":
|
||||
cr.Spec.FetchSource.Repository = args[1]
|
||||
cr.Spec.Git.Repository = tArg1
|
||||
case "accessToken":
|
||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||
key, err := qConfig.GetEncryptionKeyFor(cr.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cr.SetFetchAccessToken(args[1], key)
|
||||
return cr.SetFetchAccessToken(tArg1, key)
|
||||
case "secretName":
|
||||
cr.Spec.FetchSource.SecretName = args[1]
|
||||
cr.Spec.Git.SecretName = tArg1
|
||||
case "userName":
|
||||
cr.Spec.FetchSource.UserName = args[1]
|
||||
default:
|
||||
return errors.New(arg + " does not match any cr spec")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) processSetGit(arg string, cr *api.QliksenseCR) error {
|
||||
args := strings.Split(arg, "=")
|
||||
subs := strings.Split(args[0], ".")
|
||||
if cr.Spec.Git == nil {
|
||||
cr.Spec.Git = &config.Repo{}
|
||||
}
|
||||
switch subs[1] {
|
||||
case "repository":
|
||||
cr.Spec.Git.Repository = args[1]
|
||||
case "accessToken":
|
||||
cr.Spec.Git.AccessToken = args[1]
|
||||
case "secretName":
|
||||
cr.Spec.Git.SecretName = args[1]
|
||||
case "userName":
|
||||
cr.Spec.Git.UserName = args[1]
|
||||
cr.Spec.Git.UserName = tArg1
|
||||
default:
|
||||
return errors.New(arg + " does not match any cr spec")
|
||||
}
|
||||
|
||||
@@ -96,7 +96,6 @@ metadata:
|
||||
name: qlik-default
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik-default
|
||||
`
|
||||
qlikDefaultContext := "qlik-default"
|
||||
@@ -244,7 +243,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
||||
q: &Qliksense{
|
||||
QliksenseHome: testDir,
|
||||
},
|
||||
args: []string{"profile=minikube", "rotateKeys=yes", "storageClassName=efs", "opsRunner.enabled=yes", "opsRunner.schedule=30 * * * *", "git.repository=master", "git.userName=foo", "git.accessToken=1234"},
|
||||
args: []string{"profile=minikube", "storageClassName=efs", "opsRunner.enabled=yes", "opsRunner.schedule=30 * * * *", "git.repository=master", "git.userName=foo", "git.accessToken=1234"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -254,7 +253,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
||||
q: &Qliksense{
|
||||
QliksenseHome: testDir,
|
||||
},
|
||||
args: []string{"someconfig=somevalue, opsRunner.schedule=bar", "opsRunner.enabled=bar", "git.foo=bar", "rotateKeys=bar"},
|
||||
args: []string{"someconfig=somevalue, opsRunner.schedule=bar", "opsRunner.enabled=bar", "git.foo=bar"},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
@@ -744,7 +743,6 @@ metadata:
|
||||
name: qlik-default
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik-default
|
||||
`
|
||||
qlikDefaultContext := "qlik-default"
|
||||
@@ -763,7 +761,6 @@ metadata:
|
||||
name: qlik1
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik1`
|
||||
|
||||
contextYaml2 :=
|
||||
@@ -774,7 +771,6 @@ metadata:
|
||||
name: qlik2
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik2`
|
||||
|
||||
contextsDir := filepath.Join(testDir, contexts, "qlik1")
|
||||
|
||||
@@ -38,7 +38,11 @@ func unsetAll(qHome string, args []string) error {
|
||||
}
|
||||
// delete key inside configs if present
|
||||
// delete key inside secrets if present
|
||||
if isRemoved = unsetServiceKey(arg, qcr); !isRemoved {
|
||||
if isRemoved = unsetServiceKey(arg, qcr); isRemoved {
|
||||
//return qConfig.WriteCR(qcr)
|
||||
continue
|
||||
}
|
||||
if isRemoved = unsetTopAttrKey(arg, qcr); !isRemoved {
|
||||
return fmt.Errorf("%s not found in the context", arg)
|
||||
}
|
||||
}
|
||||
@@ -49,7 +53,7 @@ func unsetOnlyKey(key string, qcr *api.QliksenseCR) bool {
|
||||
|
||||
v := reflect.ValueOf(qcr.Spec).Elem().FieldByName(strings.Title(key))
|
||||
if v.IsValid() && v.CanSet() {
|
||||
v.SetString("")
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -99,6 +103,21 @@ func unsetServiceKey(svcKey string, qcr *api.QliksenseCR) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func unsetTopAttrKey(attKey string, qcr *api.QliksenseCR) bool {
|
||||
sk := strings.Split(attKey, ".")
|
||||
attStruct := sk[0]
|
||||
key := sk[1]
|
||||
attV := reflect.ValueOf(qcr.Spec).Elem().FieldByName(strings.Title(attStruct))
|
||||
if !attV.IsValid() || attV.IsZero() || attV.IsNil() {
|
||||
return false
|
||||
}
|
||||
v := attV.Elem().FieldByName(strings.Title(key))
|
||||
if v.IsValid() && v.CanSet() {
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func findIndex(elem string, nvs kconfig.NameValues) int {
|
||||
for i, nv := range nvs {
|
||||
if nv.Name == elem {
|
||||
|
||||
@@ -16,7 +16,9 @@ func TestUnsetAll(t *testing.T) {
|
||||
testPepareDir(qHome)
|
||||
defer os.RemoveAll(qHome)
|
||||
//fmt.Print(qHome)
|
||||
args := []string{"rotateKeys", "qliksense", "qliksense2.acceptEula3", "serviceA.acceptEula"}
|
||||
args := []string{"qliksense", "qliksense2.acceptEula3", "serviceA.acceptEula", "opsRunner.watchBranch"}
|
||||
//args := []string{"opsRunner"}
|
||||
//args := []string{"opsRunner.watchBranch"}
|
||||
if err := unsetAll(qHome, args); err != nil {
|
||||
t.Log("error during unset", err)
|
||||
t.FailNow()
|
||||
@@ -27,10 +29,6 @@ func TestUnsetAll(t *testing.T) {
|
||||
t.Log("error while getting current cr", err)
|
||||
t.FailNow()
|
||||
}
|
||||
if qcr.Spec.RotateKeys != "" {
|
||||
t.Log("Expected empty rotateKeys but got: " + qcr.Spec.RotateKeys)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if qcr.Spec.Configs["qliksense"] != nil {
|
||||
t.Log("qliksense in configs not deleted")
|
||||
@@ -44,6 +42,14 @@ func TestUnsetAll(t *testing.T) {
|
||||
t.Log("serviceA not deleted")
|
||||
t.Fail()
|
||||
}
|
||||
if qcr.Spec.OpsRunner == nil {
|
||||
t.Log("opsRunner not deleted")
|
||||
t.Fail()
|
||||
}
|
||||
if qcr.Spec.OpsRunner.WatchBranch != "" {
|
||||
t.Log("opsRunner.watchBranch not deleted")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func testPepareDir(qHome string) {
|
||||
@@ -72,7 +78,9 @@ metadata:
|
||||
name: qlik-default
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
opsRunner:
|
||||
enabled: "yes"
|
||||
watchBranch: something
|
||||
configs:
|
||||
qliksense:
|
||||
- name: acceptEula
|
||||
|
||||
@@ -93,11 +93,15 @@ func getQliksenseInitCrds(qcr *qapi.QliksenseCR) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
qInitMsPath := filepath.Join(repoPath, Q_INIT_CRD_PATH)
|
||||
qInitMsPath := filepath.Join(repoPath, "manifests", qcr.Spec.Profile, "crds")
|
||||
if _, err := os.Lstat(qInitMsPath); err != nil {
|
||||
// older version of qliksense-init used
|
||||
qInitMsPath = filepath.Join(repoPath, "manifests/base/manifests/qliksense-init")
|
||||
qInitMsPath = filepath.Join(repoPath, Q_INIT_CRD_PATH)
|
||||
if _, err := os.Lstat(qInitMsPath); err != nil {
|
||||
// older version of qliksense-init used
|
||||
qInitMsPath = filepath.Join(repoPath, "manifests/base/manifests/qliksense-init")
|
||||
}
|
||||
}
|
||||
|
||||
qInitByte, err := ExecuteKustomizeBuild(qInitMsPath)
|
||||
if err != nil {
|
||||
fmt.Println("cannot generate crds for qliksense-init", err)
|
||||
|
||||
@@ -268,7 +268,6 @@ spec:
|
||||
- name: imageRegistry
|
||||
value: %s
|
||||
manifestsRoot: %s
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik-default
|
||||
`, version, registry.url, manifestsRootDir)
|
||||
setupQliksenseTestDefaultContext(t, tmpQlikSenseHome, cr)
|
||||
|
||||
@@ -94,7 +94,6 @@ func fetchAndUpdateCR(qConfig *qapi.QliksenseConfig, version string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destDir := qConfig.BuildRepoPath(version)
|
||||
fmt.Printf("fetching version [%s] from %s\n", version, qcr.GetFetchUrl())
|
||||
if err := qapi.CopyDirectory(tempDest, destDir); err != nil {
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestFetchAndUpdateCR(t *testing.T) {
|
||||
}
|
||||
q.SetUpQliksenseContext("test1")
|
||||
qConfig := qapi.NewQConfig(tempHome)
|
||||
if err := fetchAndUpdateCR(qConfig, "v0.0.2"); err != nil {
|
||||
if err := fetchAndUpdateCR(qConfig, "v0.0.8"); err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
@@ -28,8 +28,8 @@ func TestFetchAndUpdateCR(t *testing.T) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if cr.Spec.ManifestsRoot != "contexts/test1/qlik-k8s/v0.0.2" {
|
||||
t.Log("actual path: " + cr.Spec.ManifestsRoot + ", expected path: contexts/test1/qlik-k8s/v0.0.2")
|
||||
if cr.Spec.ManifestsRoot != "contexts/test1/qlik-k8s/v0.0.8" {
|
||||
t.Log("actual path: " + cr.Spec.ManifestsRoot + ", expected path: contexts/test1/qlik-k8s/v0.0.8")
|
||||
t.FailNow()
|
||||
}
|
||||
//testing latest tag is fetched
|
||||
@@ -43,7 +43,7 @@ func TestFetchAndUpdateCR(t *testing.T) {
|
||||
cr = &qapi.QliksenseCR{}
|
||||
qapi.ReadFromFile(cr, actualCrFile)
|
||||
v := cr.GetLabelFromCr("version")
|
||||
if v == "" || v == "v0.0.2" {
|
||||
if v == "" || v == "v0.0.8" {
|
||||
t.Log("should get latest but got version: " + v)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/qlik-oss/k-apis/pkg/git"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
@@ -22,8 +24,20 @@ func (q *Qliksense) GetInstallableVersions(opts *LsRemoteCmdOptions) error {
|
||||
}
|
||||
|
||||
var repoPath string
|
||||
var auth transport.AuthMethod
|
||||
if qcr.Spec.GetManifestsRoot() != "" {
|
||||
repoPath = qcr.Spec.GetManifestsRoot()
|
||||
encKey, err := qConfig.GetEncryptionKeyFor(qcr.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessToken := qcr.GetFetchAccessToken(encKey)
|
||||
if accessToken != "" {
|
||||
auth = &http.BasicAuth{
|
||||
Username: "something",
|
||||
Password: accessToken,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repoPath, err = DownloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
|
||||
if err != nil {
|
||||
@@ -36,7 +50,7 @@ func (q *Qliksense) GetInstallableVersions(opts *LsRemoteCmdOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
remoteRefsList, err := git.GetRemoteRefs(r, nil,
|
||||
remoteRefsList, err := git.GetRemoteRefs(r, auth,
|
||||
&git.RemoteRefConstraints{
|
||||
Include: true,
|
||||
Sort: true,
|
||||
@@ -96,13 +110,18 @@ func getLatestTag(repoUrl, accessToken string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
r, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
remoteRefsList, err := git.GetRemoteRefs(r, nil,
|
||||
var auth transport.AuthMethod
|
||||
if accessToken != "" {
|
||||
auth = &http.BasicAuth{
|
||||
Username: "something",
|
||||
Password: accessToken,
|
||||
}
|
||||
}
|
||||
remoteRefsList, err := git.GetRemoteRefs(r, auth,
|
||||
&git.RemoteRefConstraints{
|
||||
Include: true,
|
||||
Sort: true,
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mattn/go-tty"
|
||||
|
||||
@@ -22,12 +24,12 @@ import (
|
||||
type InstallCommandOptions struct {
|
||||
StorageClass string
|
||||
MongodbUri string
|
||||
RotateKeys string
|
||||
AcceptEULA string
|
||||
DryRun bool
|
||||
Pull bool
|
||||
Push bool
|
||||
CleanPatchFiles bool
|
||||
RotateKeys bool
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -45,6 +47,14 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions) err
|
||||
return err
|
||||
}
|
||||
|
||||
if !qcr.IsRepoExist() {
|
||||
if err := fetchAndUpdateCR(qConfig, version); err != nil {
|
||||
return err
|
||||
} else if qcr, err = qConfig.GetCurrentCR(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.AcceptEULA != "" && opts.AcceptEULA != "yes" {
|
||||
enforceEula()
|
||||
} else if opts.AcceptEULA == "" && !qcr.IsEULA() {
|
||||
@@ -58,8 +68,9 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions) err
|
||||
if opts.StorageClass != "" {
|
||||
qcr.Spec.StorageClassName = opts.StorageClass
|
||||
}
|
||||
if opts.RotateKeys != "" {
|
||||
qcr.Spec.RotateKeys = opts.RotateKeys
|
||||
|
||||
if err := qConfig.WriteCurrentContextCR(qcr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.CleanPatchFiles {
|
||||
@@ -71,13 +82,11 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions) err
|
||||
// for debugging purpose
|
||||
if opts.DryRun {
|
||||
// generate patches
|
||||
qcr.Spec.RotateKeys = "None"
|
||||
userHomeDir, _ := homedir.Dir()
|
||||
fmt.Println("Generating patches only")
|
||||
cr.GeneratePatches(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config"))
|
||||
cr.GeneratePatches(&qcr.KApiCr, config.KeysActionDoNothing, path.Join(userHomeDir, ".kube", "config"))
|
||||
return nil
|
||||
}
|
||||
qConfig.WriteCurrentContextCR(qcr)
|
||||
|
||||
if installed, err := q.CheckAllCrdsInstalled(); err != nil {
|
||||
fmt.Println("error verifying whether CRDs are installed", err)
|
||||
@@ -123,6 +132,18 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions) err
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.RotateKeys {
|
||||
fmt.Println("Deleting stored application keys")
|
||||
if err := q.DeleteKeysClusterBackup(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
qcr.AddLabelToCr("keys-rotated", strconv.FormatInt(time.Now().Unix(), 10))
|
||||
if err := qConfig.WriteCurrentContextCR(qcr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if qcr.Spec.OpsRunner != nil {
|
||||
// fetching and applying manifest will be in the operator controller
|
||||
// get decrypted cr
|
||||
@@ -132,49 +153,19 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions) err
|
||||
return q.applyCR(dcr)
|
||||
}
|
||||
}
|
||||
if !qcr.IsRepoExist() {
|
||||
if err := fetchAndUpdateCR(qConfig, version); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if qcr.Spec.GetManifestsRoot() == "" {
|
||||
return errors.New("cannot get the manifest root. Use qliksense fetch <version> or qliksense set manifestsRoot")
|
||||
}
|
||||
|
||||
// install generated manifests into cluster
|
||||
fmt.Println("Installing generated manifests into the cluster")
|
||||
|
||||
if dcr, err := qConfig.GetDecryptedCr(qcr); err != nil {
|
||||
return err
|
||||
} else if err := q.applyConfigToK8s(dcr); err != nil {
|
||||
fmt.Println("cannot do kubectl apply on manifests")
|
||||
return err
|
||||
} else {
|
||||
if IsQliksenseInstalled(dcr.GetName()) {
|
||||
return q.UpgradeQK8s(opts.CleanPatchFiles)
|
||||
}
|
||||
if err := q.applyConfigToK8s(dcr); err != nil {
|
||||
fmt.Println("cannot do kubectl apply on manifests")
|
||||
return err
|
||||
} else {
|
||||
return q.applyCR(dcr)
|
||||
}
|
||||
return q.applyCR(dcr)
|
||||
}
|
||||
}
|
||||
|
||||
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 != "" {
|
||||
|
||||
@@ -8,13 +8,12 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func TestCreateK8sResourceBeforePatch(t *testing.T) {
|
||||
@@ -44,8 +43,7 @@ spec:
|
||||
qliksense:
|
||||
- name: mongodbUri
|
||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"`
|
||||
profile: docker-desktop`
|
||||
|
||||
q := New(testDir)
|
||||
if err := q.LoadCr([]byte(sampleCr), false); err != nil {
|
||||
|
||||
21
pkg/qliksense/keys.go
Normal file
21
pkg/qliksense/keys.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/qlik-oss/k-apis/pkg/cr"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func (q *Qliksense) DeleteKeysClusterBackup() error {
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
if qcr, err := qConfig.GetCurrentCR(); err != nil {
|
||||
return err
|
||||
} else if userHomeDir, err := homedir.Dir(); err != nil {
|
||||
return err
|
||||
} else if err := cr.DeleteKeysClusterBackup(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -276,9 +276,11 @@ func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
|
||||
configPath := filepath.Join(tmpDir, "config")
|
||||
if repo, err := kapis_git.CloneRepository(configPath, defaultConfigRepoGitUrl, nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v\n", err)
|
||||
} else if err := kapis_git.Checkout(repo, "e38df644e759abf0b5941c1511d1a2cd5e3c42fa", "", nil); err != nil {
|
||||
} else if err := kapis_git.Checkout(repo, "e38df644e759abf0b5941c1511d1a2cd5e3c42fa", "commit-e38df644e759abf0b5941c1511d1a2cd5e3c42fa", nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v\n", err)
|
||||
}
|
||||
//tmpDir := "/var/folders/mf/5hs1qkq508q_scjbhxhmf9qwjrp346/T/679268230"
|
||||
//configPath := "/var/folders/mf/5hs1qkq508q_scjbhxhmf9qwjrp346/T/679268230/config"
|
||||
|
||||
cr := &config.CRSpec{
|
||||
ManifestsRoot: configPath,
|
||||
@@ -310,8 +312,8 @@ func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
|
||||
}
|
||||
break
|
||||
}
|
||||
if resource["kind"].(string) == "Secret" && strings.Contains(resource["metadata"].(map[interface {}]interface {})["name"].(string), "users-secrets-") {
|
||||
keyIdBase64 = resource["data"].(map[interface {}]interface {})["tokenAuthPrivateKeyId"].(string)
|
||||
if resource["kind"].(string) == "Secret" && strings.Contains(resource["metadata"].(map[interface{}]interface{})["name"].(string), "users-secrets-") {
|
||||
keyIdBase64 = resource["data"].(map[interface{}]interface{})["tokenAuthPrivateKeyId"].(string)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ func (q *Qliksense) loadCrStringIntoFileSystem(crstr string, overwriteExistingCo
|
||||
}
|
||||
}
|
||||
}
|
||||
if cr.Spec.FetchSource != nil && cr.Spec.FetchSource.AccessToken != "" {
|
||||
if err := cr.SetFetchAccessToken(cr.Spec.FetchSource.AccessToken, encryptionKey); err != nil {
|
||||
if cr.Spec.Git != nil && cr.Spec.Git.AccessToken != "" {
|
||||
if err := cr.SetFetchAccessToken(cr.Spec.Git.AccessToken, encryptionKey); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,7 @@ spec:
|
||||
qliksense:
|
||||
- name: mongodbUri
|
||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"`
|
||||
profile: docker-desktop`
|
||||
sampleCr2 := `
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
@@ -61,8 +60,7 @@ spec:
|
||||
qliksense:
|
||||
- name: mongodbUri
|
||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"`
|
||||
profile: docker-desktop`
|
||||
|
||||
duplicateCr := `
|
||||
apiVersion: qlik.com/v1
|
||||
|
||||
@@ -25,7 +25,6 @@ func (q *Qliksense) UpgradeQK8s(cleanPatchFiles bool) error {
|
||||
fmt.Println("cannot get the current-context cr", err)
|
||||
return err
|
||||
}
|
||||
qcr.Spec.RotateKeys = "no"
|
||||
|
||||
dcr, err := qConfig.GetDecryptedCr(qcr)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user