Compare commits

...

15 Commits

Author SHA1 Message Date
Foysal Iqbal
0b6c1b8bc9 fix editor
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-19 14:26:03 -04:00
Foysal Iqbal
7091da099c Merge pull request #427 from qlik-oss/remove-fetchsource
remove fetch source
2020-06-18 15:00:21 -04:00
Foysal Iqbal
1f8bcf3469 Merge branch 'master' into remove-fetchsource 2020-06-18 14:22:33 -04:00
Boris Kuschel
627a4a14f9 Improve docs (#428)
* Improve docs

Signed-off-by: Boris Kuschel <dwb@qlik.com>

* Update docs/getting_started.md

* Update docs/getting_started.md

* Update docs/getting_started.md

* Update docs/getting_started.md

* Update docs/getting_started.md

* Update docs/getting_started.md

* Update docs/getting_started.md

* Update docs/getting_started.md

* Update docs/getting_started.md

* Update docs/getting_started.md

* Update docs/index.md

* Minor changes, formatting

Co-authored-by: Boris Kuschel <dwb@qlik.com>
Co-authored-by: Ilir Bekteshi <Ilir.Bekteshi@qlik.com>
2020-06-18 17:48:59 +02:00
Foysal Iqbal
66333e9e97 Merge branch 'master' into remove-fetchsource 2020-06-18 10:39:08 -04:00
Foysal Iqbal
36c91e9dab fix access token
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-18 10:30:08 -04:00
Ashwathi Shiva
b6ae0c9873 Autorun postflight checks (#429)
* postflight checks triggerred on qliksense install, updated docs, added a postflight all command
2020-06-18 10:03:55 -04:00
Andriy Bulynko
6899a7be77 go-git and test fixes (#431) 2020-06-18 08:40:38 -04:00
Foysal Iqbal
373d6499dc fix conflict
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-18 08:18:17 -04:00
Foysal Iqbal
746823c54b merge conflict 2020-06-18 02:02:42 -04:00
Andriy Bulynko
73b5da8c14 go-git and test fixes 2020-06-17 16:24:14 -04:00
Foysal Iqbal
7cc6e55779 remove fetch source
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-17 09:16:53 -04:00
Foysal Iqbal
27c8f3ee8e remove fetch source
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-16 11:46:35 -04:00
Foysal Iqbal
cb9f463f01 remove fetch source
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-06-16 10:44:08 -04:00
Foysal Iqbal
293e923c82 Merge pull request #409 from qlik-oss/fix-unset
fix unset
2020-06-12 23:30:07 -04:00
22 changed files with 345 additions and 134 deletions

View File

@@ -24,10 +24,15 @@ 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 err1 := q.InstallQK8s(version, opts); err1 != nil {
return err1
}
}
return AllPostflightChecks(q).Execute()
},
}

View File

@@ -24,7 +24,7 @@ func postflightCmd(q *qliksense.Qliksense) *cobra.Command {
return postflightCmd
}
func pfMigrationCheck(q *qliksense.Qliksense) *cobra.Command {
func postflightMigrationCheck(q *qliksense.Qliksense) *cobra.Command {
out := ansi.NewColorableStdout()
postflightOpts := &postflight.PostflightOptions{}
var postflightMigrationCmd = &cobra.Command{
@@ -58,3 +58,39 @@ func pfMigrationCheck(q *qliksense.Qliksense) *cobra.Command {
f.BoolVarP(&postflightOpts.Verbose, "verbose", "v", false, "verbose mode")
return postflightMigrationCmd
}
func AllPostflightChecks(q *qliksense.Qliksense) *cobra.Command {
out := ansi.NewColorableStdout()
postflightOpts := &postflight.PostflightOptions{}
var postflightAllChecksCmd = &cobra.Command{
Use: "all",
Short: "perform all checks",
Long: `perform all postflight checks`,
Example: `qliksense postflight all`,
RunE: func(cmd *cobra.Command, args []string) error {
pf := &postflight.QliksensePostflight{Q: q, P: postflightOpts, CG: &api.ClientGoUtils{Verbose: postflightOpts.Verbose}}
// run all postflight checks
fmt.Printf("Running all postflight checks...\n\n")
namespace, kubeConfigContents, err := pf.CG.LoadKubeConfigAndNamespace()
if err != nil {
fmt.Fprintf(out, "%s\n", Red("Unable to run all postflight checks"))
fmt.Printf("Error: %v\n", err)
return nil
}
if namespace == "" {
namespace = "default"
}
if err = pf.RunAllPostflightChecks(namespace, kubeConfigContents, postflightOpts); err != nil {
fmt.Fprintf(out, "%s\n", Red("1 or more preflight checks have FAILED"))
fmt.Printf("Completed running all postflight checks")
return nil
}
fmt.Fprintf(out, "%s\n", Green("All postflight checks have PASSED"))
return nil
},
}
f := postflightAllChecksCmd.Flags()
f.BoolVarP(&postflightOpts.Verbose, "verbose", "v", false, "verbose mode")
return postflightAllChecksCmd
}

View File

@@ -217,7 +217,8 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
// add postflight command
postflightCmd := postflightCmd(p)
postflightCmd.AddCommand(pfMigrationCheck(p))
postflightCmd.AddCommand(postflightMigrationCheck(p))
postflightCmd.AddCommand(AllPostflightChecks(p))
cmd.AddCommand(postflightCmd)
return cmd

View File

@@ -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

View File

@@ -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
```

View File

@@ -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

View File

@@ -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
```

5
go.mod
View File

@@ -22,7 +22,7 @@ require (
github.com/bugsnag/bugsnag-go v1.5.3 // indirect
github.com/containers/image/v5 v5.1.0
github.com/docker/go-metrics v0.0.1 // indirect
github.com/go-git/go-git/v5 v5.0.0
github.com/go-git/go-git/v5 v5.1.0
github.com/gobuffalo/envy v1.9.0 // indirect
github.com/gobuffalo/logger v1.0.3 // indirect
github.com/gobuffalo/packd v1.0.0 // indirect
@@ -40,7 +40,7 @@ require (
github.com/mitchellh/go-homedir v1.1.0
github.com/otiai10/copy v1.1.1
github.com/pkg/errors v0.9.1
github.com/qlik-oss/k-apis v0.1.7
github.com/qlik-oss/k-apis v0.1.10
github.com/robfig/cron/v3 v3.0.1
github.com/rogpeppe/go-internal v1.5.2 // indirect
github.com/spf13/cobra v0.0.6
@@ -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
)

8
go.sum
View File

@@ -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=
@@ -620,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=
@@ -881,8 +885,8 @@ 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.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.1.10 h1:adBXokJpE7oOr9wkPOHgpVbvuhLLKtqFdnX7V9MEyOs=
github.com/qlik-oss/k-apis v0.1.10/go.mod h1:qJVbbSYtZ+fFCojEyG9UoiCAmymm0JEtnhulr5M7HyU=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b h1:RDh3OZJOriy/ap1NUHVKsPG07N4DALaCzaqXFFK57T0=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b/go.mod h1:zh3yFgE5zFk1kreqzVyyj1eXyIxQJT53l4zSg8Wt4SA=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=

View File

@@ -14,6 +14,7 @@ markdown_extensions:
- pymdownx.inlinehilite
- pymdownx.superfences
- pymdownx.details
- pymdownx.tabbed
nav:
- Overview: index.md
- getting_started.md

View File

@@ -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
}

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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")
}
}

View 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")
}

View File

@@ -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)

View File

@@ -5,16 +5,17 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"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 (
@@ -85,6 +86,9 @@ func (q *Qliksense) applyConfigToK8s(qcr *qapi.QliksenseCR) error {
}
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"))
// apply generated manifests
@@ -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
}

View File

@@ -240,11 +240,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
}
@@ -285,52 +281,33 @@ func processSetSingleArg(arg string, cr *api.QliksenseCR) error {
}
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,rotateKeys, 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")
}

View File

@@ -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 {

View File

@@ -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()
}

View File

@@ -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,

View File

@@ -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
}
}