Compare commits
50 Commits
random-fix
...
pf_remove_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df018078d4 | ||
|
|
e5bc89c757 | ||
|
|
c84dcb03b0 | ||
|
|
5a00b5218c | ||
|
|
eb7517f88d | ||
|
|
ea048f1b5f | ||
|
|
0992286e23 | ||
|
|
6d87fadae0 | ||
|
|
04e2cc5b22 | ||
|
|
7b7cd7b4bf | ||
|
|
7a0bbcd5d8 | ||
|
|
6b6ef14fb1 | ||
|
|
fa0c6528e4 | ||
|
|
e6070a33c2 | ||
|
|
0c9f264ed2 | ||
|
|
22b9b902a9 | ||
|
|
3a49745622 | ||
|
|
5795988d01 | ||
|
|
e9b359c1bd | ||
|
|
9ab2478aba | ||
|
|
044e00afc5 | ||
|
|
2f718649f4 | ||
|
|
c6fe7084f5 | ||
|
|
e60ce7d62d | ||
|
|
0d2e436639 | ||
|
|
6093552ba9 | ||
|
|
449642e6f4 | ||
|
|
97b6cf21a7 | ||
|
|
14b6447154 | ||
|
|
1c60ce4cc0 | ||
|
|
7a8926773f | ||
|
|
0b868732a7 | ||
|
|
4f2581cde2 | ||
|
|
ca15145499 | ||
|
|
3274ebd12a | ||
|
|
505b4ef4ce | ||
|
|
a4e2b0dfe6 | ||
|
|
7cf2b00f0b | ||
|
|
d94454b832 | ||
|
|
f4d0bd87f6 | ||
|
|
645d1496d4 | ||
|
|
65ce074981 | ||
|
|
323014d137 | ||
|
|
31262df504 | ||
|
|
a15fe75b6c | ||
|
|
a59bf2d015 | ||
|
|
b36b8917da | ||
|
|
eed4d49665 | ||
|
|
34d35909a4 | ||
|
|
cb78b4da9f |
41
.github/workflows/build.yml
vendored
41
.github/workflows/build.yml
vendored
@@ -4,24 +4,43 @@ on: [pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
test:
|
||||
name: Test
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
strategy:
|
||||
matrix:
|
||||
go: [1.13.x]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go }}
|
||||
uses: actions/setup-go@v2-beta
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: setup make (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: choco install make -y
|
||||
|
||||
- run: make test
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
needs: test
|
||||
|
||||
steps:
|
||||
- name: Set up Go 1.13
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v2-beta
|
||||
with:
|
||||
go-version: 1.13
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||
- name: Set GOPATH
|
||||
# temporary fix
|
||||
# see https://github.com/actions/setup-go/issues/14
|
||||
run: |
|
||||
echo "##[set-env name=GOPATH;]$(dirname $GITHUB_WORKSPACE)"
|
||||
echo "##[add-path]$(dirname $GITHUB_WORKSPACE)/bin"
|
||||
shell: bash
|
||||
|
||||
- run: make test
|
||||
- run: make build
|
||||
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||
|
||||
- run: make xbuild-all
|
||||
|
||||
9
.github/workflows/release.yml
vendored
9
.github/workflows/release.yml
vendored
@@ -12,19 +12,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.13
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v2-beta
|
||||
with:
|
||||
go-version: 1.13
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* # Needed in makefile for versioning
|
||||
- name: Set GOPATH
|
||||
# temporary fix
|
||||
# see https://github.com/actions/setup-go/issues/14
|
||||
run: |
|
||||
echo "##[set-env name=GOPATH;]$(dirname $GITHUB_WORKSPACE)"
|
||||
echo "##[add-path]$(dirname $GITHUB_WORKSPACE)/bin"
|
||||
shell: bash
|
||||
|
||||
- run: make test
|
||||
- run: make xbuild-all
|
||||
|
||||
45
Makefile
45
Makefile
@@ -39,22 +39,30 @@ endif
|
||||
|
||||
.PHONY: build
|
||||
build: clean generate
|
||||
mkdir -p $(BINDIR)
|
||||
go run _make_support/mkdir_all/do.go $(BINDIR)
|
||||
go build -ldflags '$(LDFLAGS)' -tags "$(BUILDTAGS)" -o $(BINDIR)/$(MIXIN)$(FILE_EXT) ./cmd/$(MIXIN)
|
||||
$(MAKE) clean
|
||||
|
||||
.PHONY: test
|
||||
test: clean generate
|
||||
.PHONY: test-setup
|
||||
test-setup: clean generate
|
||||
ifeq ($(shell ${WHICH} docker-registry 2>${DEVNUL}),)
|
||||
$(eval TMP-docker-distribution := $(shell mktemp -d))
|
||||
git clone https://github.com/docker/distribution.git $(TMP-docker-distribution)/docker-distribution
|
||||
cd $(TMP-docker-distribution)/docker-distribution; git checkout -b v2.7.1; make
|
||||
cp $(TMP-docker-distribution)/docker-distribution/bin/registry pkg/qliksense/docker-registry
|
||||
-rm -rf $(TMP-docker-distribution)
|
||||
$(eval TMP-docker-distribution := $(shell go run _make_support/get_tmp_dir/do.go))
|
||||
git clone https://github.com/docker/distribution.git "$(TMP-docker-distribution)/docker-distribution"
|
||||
cd "$(TMP-docker-distribution)/docker-distribution" && git checkout -b v2.7.1 && $(MAKE)
|
||||
go run _make_support/copy/do.go --src "$(TMP-docker-distribution)/docker-distribution/bin/registry" --dst pkg/qliksense/docker-registry$(FILE_EXT)
|
||||
go run _make_support/remove_all/do.go "$(TMP-docker-distribution)"
|
||||
endif
|
||||
|
||||
.PHONY: test-short
|
||||
test-short: test-setup
|
||||
go test -short -count=1 -tags "$(BUILDTAGS)" -v ./...
|
||||
$(MAKE) clean
|
||||
|
||||
.PHONY: test
|
||||
test: test-setup
|
||||
go test -count=1 -tags "$(BUILDTAGS)" -v ./...
|
||||
$(MAKE) clean
|
||||
|
||||
xbuild-all: clean generate
|
||||
$(foreach OS, $(SUPPORTED_PLATFORMS), \
|
||||
$(foreach ARCH, $(SUPPORTED_ARCHES), \
|
||||
@@ -78,6 +86,7 @@ endif
|
||||
|
||||
generate: get-crds packr2
|
||||
go generate ./...
|
||||
go run _make_support/remove_all/do.go pkg/qliksense/crds
|
||||
|
||||
packr2:
|
||||
ifeq ($(shell ${WHICH} packr2 2>${DEVNUL}),)
|
||||
@@ -85,22 +94,22 @@ ifeq ($(shell ${WHICH} packr2 2>${DEVNUL}),)
|
||||
endif
|
||||
|
||||
clean: clean-packr
|
||||
-rm -fr pkg/qliksense/crds
|
||||
go run _make_support/remove_all/do.go pkg/qliksense/crds
|
||||
|
||||
clean-packr: packr2
|
||||
cd pkg/qliksense && packr2 clean
|
||||
|
||||
get-crds:
|
||||
ifeq ($(QLIKSENSE_OPERATOR_DIR),)
|
||||
$(eval TMP-operator := $(shell mktemp -d))
|
||||
$(eval TMP-operator := $(shell go run _make_support/get_tmp_dir/do.go))
|
||||
git clone https://github.com/qlik-oss/qliksense-operator.git -b master $(TMP-operator)/operator
|
||||
$(MAKE) QLIKSENSE_OPERATOR_DIR=$(TMP-operator)/operator get-crds
|
||||
-rm -rf $(TMP-operator)
|
||||
$(MAKE) QLIKSENSE_OPERATOR_DIR="$(TMP-operator)/operator" get-crds
|
||||
go run _make_support/remove_all/do.go "$(TMP-operator)"
|
||||
else
|
||||
mkdir -p pkg/qliksense/crds/cr
|
||||
mkdir -p pkg/qliksense/crds/crd
|
||||
mkdir -p pkg/qliksense/crds/crd-deploy
|
||||
cp $(QLIKSENSE_OPERATOR_DIR)/deploy/*.yaml pkg/qliksense/crds/crd-deploy
|
||||
cp $(QLIKSENSE_OPERATOR_DIR)/deploy/crds/*_crd.yaml pkg/qliksense/crds/crd
|
||||
cp $(QLIKSENSE_OPERATOR_DIR)/deploy/crds/*_cr.yaml pkg/qliksense/crds/cr
|
||||
go run _make_support/mkdir_all/do.go pkg/qliksense/crds/cr
|
||||
go run _make_support/mkdir_all/do.go pkg/qliksense/crds/crd
|
||||
go run _make_support/mkdir_all/do.go pkg/qliksense/crds/crd-deploy
|
||||
go run _make_support/copy/do.go --src-pattern "$(QLIKSENSE_OPERATOR_DIR)/deploy/*.yaml" --dst pkg/qliksense/crds/crd-deploy
|
||||
go run _make_support/copy/do.go --src-pattern "$(QLIKSENSE_OPERATOR_DIR)/deploy/crds/*_crd.yaml" --dst pkg/qliksense/crds/crd
|
||||
go run _make_support/copy/do.go --src-pattern "$(QLIKSENSE_OPERATOR_DIR)/deploy/crds/*_cr.yaml" --dst pkg/qliksense/crds/cr
|
||||
endif
|
||||
|
||||
10
README.md
10
README.md
@@ -1,16 +1,12 @@
|
||||
# (WIP) Qlik Sense installation and operations CLI
|
||||
# (WIP) Qlik Sense on Kubernetes installation and operations CLI
|
||||
|
||||
## Documentation
|
||||
|
||||
To learn more about Qlik Sense installer go to https://qlik-oss.github.io/sense-installer/
|
||||
To learn more about Qlik Sense on Kubernetes CLI go to https://qlik-oss.github.io/sense-installer/
|
||||
|
||||
## About
|
||||
|
||||
The Qlik Sense installer 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). This cli facilitates:
|
||||
|
||||
- installation of QSEoK
|
||||
- installation of qliksense operator to manage QSEoK
|
||||
- air gapped installation of QSEoK
|
||||
The QSEoK 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).
|
||||
|
||||
This is a technology preview that uses Qlik modified [kustomize](https://github.com/qlik-oss/kustomize) to kubernetes manifests of the versions of the [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s) repository.
|
||||
|
||||
|
||||
109
_make_support/copy/do.go
Normal file
109
_make_support/copy/do.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/otiai10/copy"
|
||||
)
|
||||
|
||||
func main() {
|
||||
srcPattern := flag.String("src-pattern", "", "Source file pattern")
|
||||
src := flag.String("src", "", "Source file or directory")
|
||||
dst := flag.String("dst", "", "Destination file or directory")
|
||||
flag.Parse()
|
||||
|
||||
if *srcPattern != "" {
|
||||
if dstInfo, err := os.Lstat(*dst); err != nil {
|
||||
panic(err)
|
||||
} else if !dstInfo.IsDir() {
|
||||
panic(fmt.Errorf("%v must be a directory", *dst))
|
||||
}
|
||||
|
||||
if matches, err := filepath.Glob(*srcPattern); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
for _, match := range matches {
|
||||
srcInfo, err := os.Lstat(match)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if srcInfo.IsDir() {
|
||||
if err := copy.Copy(match, *dst, copy.Options{
|
||||
OnSymlink: func(p string) copy.SymlinkAction {
|
||||
return copy.Skip
|
||||
},
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else if srcInfo.Mode().IsRegular() {
|
||||
if err := fcopy(match, filepath.Join(*dst, filepath.Base(match)), srcInfo); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if *src != "" {
|
||||
srcInfo, err := os.Lstat(*src)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if srcInfo.IsDir() {
|
||||
if err := copy.Copy(*src, *dst, copy.Options{
|
||||
OnSymlink: func(p string) copy.SymlinkAction {
|
||||
return copy.Skip
|
||||
},
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else if srcInfo.Mode().IsRegular() {
|
||||
finalDestination := *dst
|
||||
if dstInfo, err := os.Lstat(*dst); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
panic(err)
|
||||
}
|
||||
} else if dstInfo.IsDir() {
|
||||
finalDestination = filepath.Join(*dst, filepath.Base(*src))
|
||||
} else if dstInfo.Mode().IsRegular() {
|
||||
fmt.Println("WARNING: over-writing existing file: ", *dst)
|
||||
if err := os.Remove(*dst); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
panic(fmt.Errorf("not sure how to copy to this dst: %v", *dst))
|
||||
}
|
||||
if err := fcopy(*src, finalDestination, srcInfo); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fcopy(src, dest string, info os.FileInfo) (err error) {
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err = os.Chmod(f.Name(), info.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
_, err = io.Copy(f, s)
|
||||
return err
|
||||
}
|
||||
5
_make_support/copy/go.mod
Normal file
5
_make_support/copy/go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module github.com/qlik-oss/sense-installer/_make_support/copy
|
||||
|
||||
go 1.13
|
||||
|
||||
require github.com/otiai10/copy v1.1.1
|
||||
8
_make_support/copy/go.sum
Normal file
8
_make_support/copy/go.sum
Normal file
@@ -0,0 +1,8 @@
|
||||
github.com/otiai10/copy v1.1.1 h1:PH7IFlRQ6Fv9vYmuXbDRLdgTHoP1w483kPNUP2bskpo=
|
||||
github.com/otiai10/copy v1.1.1/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI=
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc=
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
14
_make_support/get_tmp_dir/do.go
Normal file
14
_make_support/get_tmp_dir/do.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if tmpDir, err := ioutil.TempDir("", ""); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Print(tmpDir)
|
||||
}
|
||||
}
|
||||
3
_make_support/get_tmp_dir/go.mod
Normal file
3
_make_support/get_tmp_dir/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/qlik-oss/sense-installer/_make_support/get_tmp_dir
|
||||
|
||||
go 1.13
|
||||
11
_make_support/mkdir_all/do.go
Normal file
11
_make_support/mkdir_all/do.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := os.MkdirAll(os.Args[1], os.ModePerm); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
3
_make_support/mkdir_all/go.mod
Normal file
3
_make_support/mkdir_all/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/qlik-oss/sense-installer/_make_support/mkdir_all
|
||||
|
||||
go 1.13
|
||||
11
_make_support/remove_all/do.go
Normal file
11
_make_support/remove_all/do.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := os.RemoveAll(os.Args[1]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
3
_make_support/remove_all/go.mod
Normal file
3
_make_support/remove_all/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/qlik-oss/sense-installer/_make_support/remove_all
|
||||
|
||||
go 1.13
|
||||
@@ -39,7 +39,7 @@ qliksense about --profile=test
|
||||
then get version information based on the default profile in the qliksense-k8s repo master
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if gitRef, err := getAboutCommandGitRef(args); err != nil {
|
||||
if gitRef, err := getSingleArg(args); err != nil {
|
||||
return err
|
||||
} else if vout, err := q.About(gitRef, opts.Profile); err != nil {
|
||||
return err
|
||||
@@ -56,7 +56,7 @@ qliksense about --profile=test
|
||||
return c
|
||||
}
|
||||
|
||||
func getAboutCommandGitRef(args []string) (string, error) {
|
||||
func getSingleArg(args []string) (string, error) {
|
||||
if len(args) > 1 {
|
||||
return "", errors.New("too many arguments, only 1 expected")
|
||||
} else if len(args) == 1 {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -10,15 +14,21 @@ import (
|
||||
func applyCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
opts := &qliksense.InstallCommandOptions{}
|
||||
filePath := ""
|
||||
keepPatchFiles := false
|
||||
keepPatchFiles, pull, push := false, false, false
|
||||
c := &cobra.Command{
|
||||
Use: "apply",
|
||||
Short: "install qliksense based on provided cr file",
|
||||
Long: `install qliksense based on provided cr file`,
|
||||
Example: `qliksense apply -f file_name or cat cr_file | qliksense apply -f -`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runLoadOrApplyCommandE(cmd, func(reader io.Reader) error {
|
||||
return q.ApplyCRFromReader(reader, opts, keepPatchFiles, true)
|
||||
return runLoadOrApplyCommandE(cmd, func(crBytes []byte) error {
|
||||
if cr, crBytesWithEula, err := getCrWithEulaInserted(crBytes); err != nil {
|
||||
return err
|
||||
} else if err := validatePullPushFlagsOnApply(cr, pull, push); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return q.ApplyCRFromReader(bytes.NewReader(crBytesWithEula), opts, keepPatchFiles, true, pull, push)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -30,8 +40,35 @@ func applyCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
f.StringVarP(&opts.MongoDbUri, "mongoDbUri", "m", "", "mongoDbUri for qliksense (i.e. mongodb://qlik-default-mongodb:27017/qliksense?ssl=false)")
|
||||
f.StringVarP(&opts.RotateKeys, "rotateKeys", "r", "", "Rotate JWT keys for qliksense (yes:rotate keys/ no:use exising keys from cluster/ None: use default EJSON_KEY from env")
|
||||
f.BoolVar(&keepPatchFiles, keepPatchFilesFlagName, keepPatchFiles, keepPatchFilesFlagUsage)
|
||||
f.BoolVarP(&pull, pullFlagName, pullFlagShorthand, pull, pullFlagUsage)
|
||||
f.BoolVarP(&push, pushFlagName, pushFlagShorthand, push, pushFlagUsage)
|
||||
|
||||
eulaPreRunHooks.addValidator(c.Name(), loadOrApplyCommandEulaPreRunHook)
|
||||
eulaPreRunHooks.addValidator(fmt.Sprintf("%v %v", rootCommandName, c.Name()), loadOrApplyCommandEulaPreRunHook)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func validatePullPushFlagsOnApply(cr *qapi.QliksenseCR, pull, push bool) error {
|
||||
if pull && !push {
|
||||
fmt.Printf("WARNING: pulling images without pushing them")
|
||||
}
|
||||
if push {
|
||||
if registry := cr.Spec.GetImageRegistry(); registry == "" {
|
||||
return errors.New("no image registry set in the CR; to set it use: qliksense config set-image-registry")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCrWithEulaInserted(crBytes []byte) (*qapi.QliksenseCR, []byte, error) {
|
||||
if cr, err := qapi.CreateCRObjectFromString(string(crBytes)); err != nil {
|
||||
return nil, nil, err
|
||||
} else {
|
||||
cr.SetEULA("yes")
|
||||
if crBytesWithEula, err := qapi.K8sToYaml(cr); err != nil {
|
||||
return nil, nil, err
|
||||
} else {
|
||||
return cr, crBytesWithEula, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
|
||||
@@ -68,18 +69,30 @@ func setConfigsCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
var (
|
||||
cmd *cobra.Command
|
||||
)
|
||||
|
||||
base64Encoded := false
|
||||
cmd = &cobra.Command{
|
||||
Use: "set-configs",
|
||||
Short: "set configurations into the qliksense context as key-value pairs",
|
||||
Example: `
|
||||
qliksense config set-configs <service_name>.<attribute>="<value>"
|
||||
- The above configuration will be displayed in the CR
|
||||
- The above configuration will be displayed in the CR
|
||||
qliksense config set-configs <service_name>.<attribute>="<value" --base64
|
||||
- if the value is base64 encoded
|
||||
echo "something" | base64 | qliksense config set-configs <service_name>.<attribute> --base64
|
||||
- value is coming from input pipe as base64 encoded
|
||||
echo "something" | qliksense config set-configs <service_name>.<attribute>
|
||||
- value is coming from input pipe
|
||||
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return q.SetConfigs(args)
|
||||
if isInputFromPipe() && len(args) == 1 {
|
||||
return q.SetConfigFromReader(args[0], os.Stdin, base64Encoded)
|
||||
}
|
||||
return q.SetConfigs(args, base64Encoded)
|
||||
},
|
||||
}
|
||||
f := cmd.Flags()
|
||||
f.BoolVarP(&base64Encoded, "base64", "", false, "if the arguments value is base64 encoded")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -88,7 +101,7 @@ func setSecretsCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
cmd *cobra.Command
|
||||
secret bool
|
||||
)
|
||||
|
||||
base64Encoded := false
|
||||
cmd = &cobra.Command{
|
||||
Use: "set-secrets",
|
||||
Short: "set secrets configurations into the qliksense context as key-value pairs",
|
||||
@@ -101,13 +114,24 @@ qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=true
|
||||
qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=false
|
||||
- Encrypt the secret value and display it in the current context
|
||||
- No secret resource is created
|
||||
- The above configuration will be displayed in the CR `,
|
||||
- The above configuration will be displayed in the CR
|
||||
qliksense config set-secrets <service_name>.<attribute>="<value>" --base64
|
||||
- the <value> is base64 encoded
|
||||
echo "something" | base64 | qliksense config set-secrets <service_name>.<attribute> --base64
|
||||
- value coming from input pipe as base64 encoded
|
||||
echo "something" | qliksense config set-secrets <service_name>.<attribute>
|
||||
- value coming from input pipe`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return q.SetSecrets(args, secret)
|
||||
if isInputFromPipe() && len(args) == 1 {
|
||||
return q.SetSecretsFromReader(args[0], os.Stdin, secret, base64Encoded)
|
||||
}
|
||||
return q.SetSecrets(args, secret, base64Encoded)
|
||||
},
|
||||
}
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&secret, "secret", false, "Whether secrets should be encrypted as a Kubernetes Secret resource")
|
||||
f.BoolVarP(&base64Encoded, "base64", "", false, "if the arguments value is base64 encoded")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -115,15 +139,18 @@ func deleteContextConfigCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
var (
|
||||
cmd *cobra.Command
|
||||
)
|
||||
|
||||
skipConfirmation := false
|
||||
cmd = &cobra.Command{
|
||||
Use: "delete-context",
|
||||
Short: "deletes a specific context locally (not in-cluster)",
|
||||
Example: `qliksense config delete-contexts <context_name>`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return q.DeleteContextConfig(args)
|
||||
return q.DeleteContextConfig(args, skipConfirmation)
|
||||
},
|
||||
}
|
||||
f := cmd.Flags()
|
||||
|
||||
f.BoolVar(&skipConfirmation, "yes", skipConfirmation, "skips confirmation")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -48,13 +48,14 @@ var eulaPreRunHooks = eulaPreRunHooksT{
|
||||
}
|
||||
|
||||
func commandAlwaysRequiresEulaAcceptance(commandName string) bool {
|
||||
return commandName == "install" || commandName == "upgrade" || commandName == "apply"
|
||||
return commandName == fmt.Sprintf("%v install", rootCommandName) ||
|
||||
commandName == fmt.Sprintf("%v apply", rootCommandName)
|
||||
}
|
||||
|
||||
func globalEulaPreRun(cmd *cobra.Command, q *qliksense.Qliksense) {
|
||||
if isEulaEnforced(cmd.Name()) {
|
||||
if isEulaEnforced(cmd.CommandPath()) {
|
||||
if strings.TrimSpace(strings.ToLower(cmd.Flag("acceptEULA").Value.String())) != "yes" {
|
||||
if eulaPreRunHook := eulaPreRunHooks.getValidator(cmd.Name()); eulaPreRunHook != nil {
|
||||
if eulaPreRunHook := eulaPreRunHooks.getValidator(cmd.CommandPath()); eulaPreRunHook != nil {
|
||||
if eulaAccepted, err := eulaPreRunHook(cmd, q); err != nil {
|
||||
panic(err)
|
||||
} else if !eulaAccepted {
|
||||
@@ -70,7 +71,7 @@ func globalEulaPreRun(cmd *cobra.Command, q *qliksense.Qliksense) {
|
||||
}
|
||||
|
||||
func globalEulaPostRun(cmd *cobra.Command, q *qliksense.Qliksense) {
|
||||
if isEulaEnforced(cmd.Name()) {
|
||||
if isEulaEnforced(cmd.CommandPath()) {
|
||||
if err := q.SetEulaAccepted(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func fetchCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
opts := &qliksense.FetchCommandOptions{}
|
||||
c := &cobra.Command{
|
||||
Use: "fetch",
|
||||
Short: "fetch a release from qliksense-k8s repo",
|
||||
Long: `fetch a release from qliksense-k8s repo`,
|
||||
Example: `qliksense fetch <version>`,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.New("requires a version (i.e. v1.0.0)")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Short: "fetch a release from qliksense-k8s repo, if version not supplied, will use from context",
|
||||
Long: `fetch a release from qliksense-k8s repo, if version not supplied, will use from context`,
|
||||
Example: `qliksense fetch [version]`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return q.FetchQK8s(args[0])
|
||||
if len(args) == 1 {
|
||||
opts.Version = args[0]
|
||||
}
|
||||
return q.FetchK8sWithOpts(opts)
|
||||
},
|
||||
}
|
||||
|
||||
f := c.Flags()
|
||||
f.StringVarP(&opts.GitUrl, "url", "", "", "git url from where configuration will be pulled")
|
||||
f.StringVarP(&opts.AccessToken, "accessToken", "", "", "access token for git url")
|
||||
f.StringVarP(&opts.SecretName, "secretName", "", "", "kubernetes secret name where a key name accessToken exist")
|
||||
f.BoolVarP(&opts.Overwrite, "overwrite", "", false, "Ovewrite previously fetched veersion as well as local chagnes")
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -1,32 +1,95 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func installCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
opts := &qliksense.InstallCommandOptions{}
|
||||
keepPatchFiles := false
|
||||
filePath := ""
|
||||
keepPatchFiles, pull, push := false, false, false
|
||||
c := &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "install a qliksense release",
|
||||
Long: `install a qliksense release`,
|
||||
Example: `qliksense install <version> #if no version provides, expect manifestsRoot is set somewhere in the file system`,
|
||||
Use: "install",
|
||||
Short: "install a qliksense release",
|
||||
Long: `install a qliksense release`,
|
||||
Example: `qliksense install <version> #if no version provides, expect manifestsRoot is set somewhere in the file system
|
||||
# qliksense install -f file_name or cat cr_file | qliksense install -f -
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
version := ""
|
||||
if len(args) != 0 {
|
||||
version = args[0]
|
||||
if filePath != "" {
|
||||
return runLoadOrApplyCommandE(cmd, func(crBytes []byte) error {
|
||||
if cr, crBytesWithEula, err := getCrWithEulaInserted(crBytes); err != nil {
|
||||
return err
|
||||
} else if err := validatePullPushFlagsOnApply(cr, pull, push); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return q.ApplyCRFromReader(bytes.NewReader(crBytesWithEula), opts, keepPatchFiles, true, pull, push)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
version := ""
|
||||
if len(args) != 0 {
|
||||
version = args[0]
|
||||
}
|
||||
if err := validatePullPushFlagsOnInstall(q, pull, push); err != nil {
|
||||
return err
|
||||
}
|
||||
if pull {
|
||||
fmt.Println("Pulling images...")
|
||||
if err := q.PullImages(version, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if push {
|
||||
fmt.Println("Pushing images...")
|
||||
if err := q.PushImagesForCurrentCR(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return q.InstallQK8s(version, opts, keepPatchFiles)
|
||||
}
|
||||
return q.InstallQK8s(version, opts, keepPatchFiles)
|
||||
},
|
||||
}
|
||||
|
||||
eulaPreRunHooks.addValidator(fmt.Sprintf("%v %v", rootCommandName, c.Name()), func(cmd *cobra.Command, q *qliksense.Qliksense) (b bool, err error) {
|
||||
if filePath != "" {
|
||||
return loadOrApplyCommandEulaPreRunHook(cmd, q)
|
||||
} else if qConfig, err := qapi.NewQConfigE(q.QliksenseHome); err != nil {
|
||||
return false, err
|
||||
} else if qcr, err := qConfig.GetCurrentCR(); err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
return qcr.IsEULA(), nil
|
||||
}
|
||||
})
|
||||
|
||||
f := c.Flags()
|
||||
f.StringVarP(&opts.StorageClass, "storageClass", "s", "", "Storage class for qliksense")
|
||||
f.StringVarP(&opts.MongoDbUri, "mongoDbUri", "m", "", "mongoDbUri for qliksense (i.e. mongodb://qlik-default-mongodb:27017/qliksense?ssl=false)")
|
||||
f.StringVarP(&opts.RotateKeys, "rotateKeys", "r", "", "Rotate JWT keys for qliksense (yes:rotate keys/ no:use exising keys from cluster/ None: use default EJSON_KEY from env")
|
||||
f.BoolVar(&keepPatchFiles, keepPatchFilesFlagName, keepPatchFiles, keepPatchFilesFlagUsage)
|
||||
f.StringVarP(&filePath, "file", "f", "", "Install from a CR file")
|
||||
|
||||
f.BoolVarP(&pull, pullFlagName, pullFlagShorthand, pull, pullFlagUsage)
|
||||
f.BoolVarP(&push, pushFlagName, pushFlagShorthand, push, pushFlagUsage)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func validatePullPushFlagsOnInstall(q *qliksense.Qliksense, pull, push bool) error {
|
||||
if pull && !push {
|
||||
fmt.Printf("WARNING: pulling images without pushing them")
|
||||
}
|
||||
if push {
|
||||
if err := ensureImageRegistrySetInCR(q); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
@@ -20,8 +20,8 @@ func loadCrFile(q *qliksense.Qliksense) *cobra.Command {
|
||||
Long: `load a CR a file and create necessary structure for future use`,
|
||||
Example: `qliksense load -f file_name or cat cr_file | qliksense load -f -`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runLoadOrApplyCommandE(cmd, func(reader io.Reader) error {
|
||||
return q.LoadCr(reader, overwriteExistingContext)
|
||||
return runLoadOrApplyCommandE(cmd, func(buffer []byte) error {
|
||||
return q.LoadCr(bytes.NewReader(buffer), overwriteExistingContext)
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func loadCrFile(q *qliksense.Qliksense) *cobra.Command {
|
||||
c.MarkFlagRequired("file")
|
||||
f.BoolVarP(&overwriteExistingContext, "overwrite", "o", overwriteExistingContext, "Overwrite any existing contexts with the same name")
|
||||
|
||||
eulaPreRunHooks.addValidator(c.Name(), loadOrApplyCommandEulaPreRunHook)
|
||||
eulaPreRunHooks.addValidator(fmt.Sprintf("%v %v", rootCommandName, c.Name()), loadOrApplyCommandEulaPreRunHook)
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -70,15 +70,19 @@ func loadOrApplyCommandEulaPreRunHook(cmd *cobra.Command, q *qliksense.Qliksense
|
||||
}
|
||||
}
|
||||
|
||||
func runLoadOrApplyCommandE(cmd *cobra.Command, callBack func(io.Reader) error) error {
|
||||
func runLoadOrApplyCommandE(cmd *cobra.Command, callBack func(buffer []byte) error) error {
|
||||
if crBytes := eulaPreRunHooks.getPostValidationArtifact("CR"); crBytes != nil {
|
||||
return callBack(bytes.NewBuffer(crBytes.([]byte)))
|
||||
return callBack(crBytes.([]byte))
|
||||
} else {
|
||||
file, err := getCrFileFromFlag(cmd, "file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return callBack(file)
|
||||
if crBytes, err := ioutil.ReadAll(file); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return callBack(crBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
ansi "github.com/mattn/go-colorable"
|
||||
"github.com/qlik-oss/sense-installer/pkg/preflight"
|
||||
"github.com/ttacon/chalk"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -21,312 +22,417 @@ func preflightCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
}
|
||||
|
||||
func pfDnsCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
var preflightDnsCmd = &cobra.Command{
|
||||
Use: "dns",
|
||||
Short: "perform preflight dns check",
|
||||
Long: `perform preflight dns check to check DNS connectivity status in the cluster`,
|
||||
Example: `qliksense preflight dns`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
||||
|
||||
// Preflight DNS check
|
||||
fmt.Printf("Preflight DNS check\n")
|
||||
fmt.Println("---------------------")
|
||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||
if err != nil {
|
||||
fmt.Printf("Preflight DNS check FAILED\n")
|
||||
log.Fatal(err)
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight DNS check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
if err = qp.CheckDns(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Print("Preflight DNS check FAILED\n")
|
||||
log.Fatal()
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight DNS check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", chalk.Green.Color("Preflight DNS check PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := preflightDnsCmd.Flags()
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return preflightDnsCmd
|
||||
}
|
||||
|
||||
func pfK8sVersionCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
|
||||
var preflightCheckK8sVersionCmd = &cobra.Command{
|
||||
Use: "kube-version",
|
||||
Short: "check kubernetes version",
|
||||
Long: `check minimum valid kubernetes version on the cluster`,
|
||||
Example: `qliksense preflight kube-version`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
||||
|
||||
// Preflight Kubernetes minimum version check
|
||||
fmt.Printf("Preflight kubernetes minimum version check\n")
|
||||
fmt.Println("------------------------------------------")
|
||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||
if err != nil {
|
||||
fmt.Printf("Preflight kubernetes minimum version check FAILED\n")
|
||||
log.Fatal(err)
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight kubernetes minimum version check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if err = qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Printf("Preflight kubernetes minimum version check FAILED\n")
|
||||
log.Fatal()
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight kubernetes minimum version check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", chalk.Green.Color("Preflight kubernetes minimum version check PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := preflightCheckK8sVersionCmd.Flags()
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
|
||||
return preflightCheckK8sVersionCmd
|
||||
}
|
||||
|
||||
func pfAllChecksCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
var mongodbUrl string
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
|
||||
var preflightAllChecksCmd = &cobra.Command{
|
||||
Use: "all",
|
||||
Short: "perform all checks",
|
||||
Long: `perform all preflight checks on the target cluster`,
|
||||
Example: `qliksense preflight all`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
||||
|
||||
// Preflight run all checks
|
||||
fmt.Printf("Running all preflight checks\n")
|
||||
fmt.Printf("Running all preflight checks...\n\n")
|
||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Printf("Running preflight check suite has FAILED...\n")
|
||||
log.Fatal()
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Unable to run the preflight checks suite"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
qp.RunAllPreflightChecks(namespace, kubeConfigContents, mongodbUrl)
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
if err = qp.RunAllPreflightChecks(kubeConfigContents, namespace, preflightOpts); err != nil {
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("1 or more preflight checks have FAILED"))
|
||||
fmt.Println("Completed running all preflight checks")
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n\n", chalk.Green.Color("All preflight checks have PASSED"))
|
||||
return nil
|
||||
|
||||
},
|
||||
}
|
||||
f := preflightAllChecksCmd.Flags()
|
||||
f.StringVarP(&mongodbUrl, "mongodb-url", "", "", "mongodbUrl to try connecting to")
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
f.StringVarP(&preflightOpts.MongoOptions.MongodbUrl, "mongodb-url", "", "", "mongodbUrl to try connecting to")
|
||||
f.StringVarP(&preflightOpts.MongoOptions.Username, "mongodb-username", "", "", "username to connect to mongodb")
|
||||
f.StringVarP(&preflightOpts.MongoOptions.Password, "mongodb-password", "", "", "password to connect to mongodb")
|
||||
f.StringVarP(&preflightOpts.MongoOptions.CaCertFile, "mongodb-ca-cert", "", "", "certificate to use for mongodb check")
|
||||
f.StringVarP(&preflightOpts.MongoOptions.ClientCertFile, "mongodb-client-cert", "", "", "client-certificate to use for mongodb check")
|
||||
f.BoolVar(&preflightOpts.MongoOptions.Tls, "mongodb-tls", false, "enable tls?")
|
||||
|
||||
return preflightAllChecksCmd
|
||||
}
|
||||
|
||||
func pfDeploymentCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
var pfDeploymentCheckCmd = &cobra.Command{
|
||||
Use: "deployment",
|
||||
Short: "perform preflight deploymwnt check",
|
||||
Long: `perform preflight deployment check to ensure that we can create deployments in the cluster`,
|
||||
Example: `qliksense preflight deployment`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
||||
|
||||
// Preflight deployments check
|
||||
fmt.Printf("Preflight deployment check\n")
|
||||
fmt.Println("--------------------------")
|
||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||
if err != nil {
|
||||
fmt.Printf("Preflight deployment check FAILED\n")
|
||||
log.Fatal(err)
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight deployment check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
if err = qp.CheckDeployment(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Print("Preflight deploy check FAILED\n")
|
||||
log.Fatal()
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight deployment check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", chalk.Green.Color("Preflight deployment check PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := pfDeploymentCheckCmd.Flags()
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return pfDeploymentCheckCmd
|
||||
}
|
||||
|
||||
func pfServiceCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
|
||||
var pfServiceCheckCmd = &cobra.Command{
|
||||
Use: "service",
|
||||
Short: "perform preflight service check",
|
||||
Long: `perform preflight service check to ensure that we are able to create services in the cluster`,
|
||||
Example: `qliksense preflight service`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
||||
|
||||
// Preflight service check
|
||||
fmt.Printf("Preflight service check\n")
|
||||
fmt.Println("-----------------------")
|
||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||
if err != nil {
|
||||
fmt.Printf("Preflight service check FAILED\n")
|
||||
log.Fatal(err)
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight service check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
if err = qp.CheckService(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Print("Preflight service check FAILED\n")
|
||||
log.Fatal()
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight service check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", chalk.Green.Color("Preflight service check PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := pfServiceCheckCmd.Flags()
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return pfServiceCheckCmd
|
||||
}
|
||||
|
||||
func pfPodCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
|
||||
var pfPodCheckCmd = &cobra.Command{
|
||||
Use: "pod",
|
||||
Short: "perform preflight pod check",
|
||||
Long: `perform preflight pod check to ensure we can create pods in the cluster`,
|
||||
Example: `qliksense preflight pod`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
||||
|
||||
// Preflight pod check
|
||||
fmt.Printf("Preflight pod check\n")
|
||||
fmt.Println("--------------------")
|
||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||
if err != nil {
|
||||
fmt.Printf("Preflight pod check FAILED\n")
|
||||
log.Fatal(err)
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight pod check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
if err = qp.CheckPod(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Print("Preflight pod check FAILED\n")
|
||||
log.Fatal()
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight pod check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", chalk.Green.Color("Preflight pod check PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := pfPodCheckCmd.Flags()
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return pfPodCheckCmd
|
||||
}
|
||||
|
||||
func pfCreateRoleCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
|
||||
var preflightRoleCmd = &cobra.Command{
|
||||
Use: "role",
|
||||
Short: "preflight create role check",
|
||||
Long: `perform preflight role check to ensure we are able to create a role in the cluster`,
|
||||
Example: `qliksense preflight createRole`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
||||
|
||||
// Preflight role check
|
||||
fmt.Printf("Preflight role check\n")
|
||||
fmt.Println("---------------------------")
|
||||
namespace, _, err := preflight.InitPreflight()
|
||||
if err != nil {
|
||||
fmt.Printf("Preflight role check FAILED\n")
|
||||
log.Fatal(err)
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight role check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if err = qp.CheckCreateRole(namespace); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Print("Preflight role FAILED\n")
|
||||
log.Fatal()
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight role check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", chalk.Green.Color("Preflight role check PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := preflightRoleCmd.Flags()
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return preflightRoleCmd
|
||||
}
|
||||
|
||||
func pfCreateRoleBindingCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
|
||||
var preflightRoleBindingCmd = &cobra.Command{
|
||||
Use: "rolebinding",
|
||||
Short: "preflight create rolebinding check",
|
||||
Long: `perform preflight rolebinding check to ensure we are able to create a rolebinding in the cluster`,
|
||||
Example: `qliksense preflight rolebinding`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
||||
|
||||
// Preflight createRoleBinding check
|
||||
fmt.Printf("Preflight rolebinding check\n")
|
||||
fmt.Println("---------------------------")
|
||||
namespace, _, err := preflight.InitPreflight()
|
||||
if err != nil {
|
||||
fmt.Printf("Preflight rolebinding check FAILED\n")
|
||||
log.Fatal(err)
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight rolebinding check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if err = qp.CheckCreateRoleBinding(namespace); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Print("Preflight rolebinding check FAILED\n")
|
||||
log.Fatal()
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight rolebinding check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", chalk.Green.Color("Preflight rolebinding check PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := preflightRoleBindingCmd.Flags()
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return preflightRoleBindingCmd
|
||||
}
|
||||
|
||||
func pfCreateServiceAccountCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
|
||||
var preflightServiceAccountCmd = &cobra.Command{
|
||||
Use: "serviceaccount",
|
||||
Short: "preflight create ServiceAccount check",
|
||||
Long: `perform preflight serviceaccount check to ensure we are able to create a service account in the cluster`,
|
||||
Example: `qliksense preflight serviceaccount`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
||||
|
||||
// Preflight createServiceAccount check
|
||||
fmt.Printf("Preflight ServiceAccount check\n")
|
||||
fmt.Println("-------------------------------------")
|
||||
namespace, _, err := preflight.InitPreflight()
|
||||
if err != nil {
|
||||
fmt.Printf("Preflight serviceaccount check FAILED\n")
|
||||
log.Fatal(err)
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight ServiceAccount check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if err = qp.CheckCreateServiceAccount(namespace); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Print("Preflight serviceaccount check FAILED\n")
|
||||
log.Fatal()
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight ServiceAccount check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", chalk.Green.Color("Preflight ServiceAccount check PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := preflightServiceAccountCmd.Flags()
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return preflightServiceAccountCmd
|
||||
}
|
||||
|
||||
func pfCreateRBCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
var preflightCreateRBCmd = &cobra.Command{
|
||||
func pfCreateAuthCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
var preflightCreateAuthCmd = &cobra.Command{
|
||||
Use: "authcheck",
|
||||
Short: "preflight authcheck",
|
||||
Long: `perform preflight authcheck that combines the role, rolebinding and serviceaccount checks`,
|
||||
Example: `qliksense preflight authcheck`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
||||
|
||||
// Preflight authcheck
|
||||
fmt.Printf("Preflight authcheck\n")
|
||||
fmt.Println("------------------------")
|
||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||
if err != nil {
|
||||
fmt.Printf("Preflight authcheck FAILED\n")
|
||||
log.Fatal(err)
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight authcheck FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if err = qp.CheckCreateRB(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Print("Preflight authcheck FAILED\n")
|
||||
log.Fatal()
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight authcheck FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", chalk.Green.Color("Preflight authcheck PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return preflightCreateRBCmd
|
||||
f := preflightCreateAuthCmd.Flags()
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
return preflightCreateAuthCmd
|
||||
}
|
||||
|
||||
func pfMongoCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
var mongodbUrl string
|
||||
out := ansi.NewColorableStdout()
|
||||
preflightOpts := &preflight.PreflightOptions{
|
||||
MongoOptions: &preflight.MongoOptions{},
|
||||
}
|
||||
|
||||
var preflightMongoCmd = &cobra.Command{
|
||||
Use: "mongo",
|
||||
Short: "preflight mongo OR preflight mongo --url=<url>",
|
||||
Long: `perform preflight mongo check to ensure we are able to connect to a mongodb instance in the cluster`,
|
||||
Example: `qliksense preflight mongo OR preflight mongo --url=<url>`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qp := &preflight.QliksensePreflight{Q: q}
|
||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
||||
|
||||
// Preflight mongo check
|
||||
fmt.Printf("Preflight mongo check\n")
|
||||
fmt.Println("-------------------------------------")
|
||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||
if err != nil {
|
||||
fmt.Printf("Preflight mongo check FAILED\n")
|
||||
log.Fatal(err)
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight mongo check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
if err = qp.CheckMongo(kubeConfigContents, namespace, mongodbUrl); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Print("Preflight mongo check FAILED\n")
|
||||
log.Fatal()
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
if err = qp.CheckMongo(kubeConfigContents, namespace, preflightOpts); err != nil {
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight mongo check FAILED"))
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", chalk.Green.Color("Preflight mongo check PASSED"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := preflightMongoCmd.Flags()
|
||||
f.StringVarP(&mongodbUrl, "url", "", "", "mongodbUrl to try connecting to")
|
||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||
f.StringVarP(&preflightOpts.MongoOptions.MongodbUrl, "url", "", "", "mongodbUrl to try connecting to")
|
||||
f.StringVarP(&preflightOpts.MongoOptions.Username, "username", "", "", "username to connect to mongodb")
|
||||
f.StringVarP(&preflightOpts.MongoOptions.Password, "password", "", "", "password to connect to mongodb")
|
||||
f.StringVarP(&preflightOpts.MongoOptions.CaCertFile, "ca-cert", "", "", "ca certificate to use for mongodb check")
|
||||
f.StringVarP(&preflightOpts.MongoOptions.ClientCertFile, "client-cert", "", "", "client-certificate to use for mongodb check")
|
||||
f.BoolVar(&preflightOpts.MongoOptions.Tls, "tls", false, "enable tls?")
|
||||
return preflightMongoCmd
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func pullQliksenseImages(q *qliksense.Qliksense) *cobra.Command {
|
||||
Short: "Pull docker images for offline install",
|
||||
Example: `qliksense pull`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
version, err := getAboutCommandGitRef(args)
|
||||
version, err := getSingleArg(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -34,11 +34,8 @@ func pushQliksenseImages(q *qliksense.Qliksense) *cobra.Command {
|
||||
Short: "Push docker images for offline install",
|
||||
Example: `qliksense push`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
if qcr, err := qConfig.GetCurrentCR(); err != nil {
|
||||
if err := ensureImageRegistrySetInCR(q); err != nil {
|
||||
return err
|
||||
} else if registry := qcr.GetImageRegistry(); registry == "" {
|
||||
return errors.New("no image registry in config")
|
||||
} else {
|
||||
return q.PushImagesForCurrentCR()
|
||||
}
|
||||
@@ -46,3 +43,13 @@ func pushQliksenseImages(q *qliksense.Qliksense) *cobra.Command {
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func ensureImageRegistrySetInCR(q *qliksense.Qliksense) error {
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
if qcr, err := qConfig.GetCurrentCR(); err != nil {
|
||||
return err
|
||||
} else if registry := qcr.Spec.GetImageRegistry(); registry == "" {
|
||||
return errors.New("no image registry set in the CR; to set it use: qliksense config set-image-registry")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/qlik-oss/sense-installer/pkg"
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"github.com/qlik-oss/sense-installer/pkg/preflight"
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@@ -28,6 +27,13 @@ const (
|
||||
qlikSenseDirVar = ".qliksense"
|
||||
keepPatchFilesFlagName = "keep-config-repo-patches"
|
||||
keepPatchFilesFlagUsage = "Keep config repo patch files (for debugging)"
|
||||
pullFlagName = "pull"
|
||||
pullFlagShorthand = "d"
|
||||
pullFlagUsage = "If using private docker registry, pull (download) all required Qliksense images before install"
|
||||
pushFlagName = "push"
|
||||
pushFlagShorthand = "u"
|
||||
pushFlagUsage = "If using private docker registry, push (upload) all downloaded Qliksense images to that registry before install"
|
||||
rootCommandName = "qliksense"
|
||||
)
|
||||
|
||||
func initAndExecute() error {
|
||||
@@ -86,22 +92,25 @@ var versionCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func commandUsesContext(commandName string) bool {
|
||||
return commandName != "" && commandName != "qliksense" && commandName != "help" && commandName != "version"
|
||||
return commandName != "" &&
|
||||
commandName != rootCommandName &&
|
||||
commandName != fmt.Sprintf("%v help", rootCommandName) &&
|
||||
commandName != fmt.Sprintf("%v version", rootCommandName)
|
||||
}
|
||||
|
||||
func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "qliksense",
|
||||
Use: rootCommandName,
|
||||
Short: "Qliksense cli tool",
|
||||
Long: `qliksense cli tool provides functionality to perform operations on qliksense-k8s, qliksense operator, and kubernetes cluster`,
|
||||
Args: cobra.ArbitraryArgs,
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
if commandUsesContext(cmd.Name()) {
|
||||
if commandUsesContext(cmd.CommandPath()) {
|
||||
globalEulaPreRun(cmd, p)
|
||||
if err := p.SetUpQliksenseDefaultContext(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pf := preflight.NewPreflightConfig(p.QliksenseHome)
|
||||
pf := api.NewPreflightConfig(p.QliksenseHome)
|
||||
if err := pf.Initialize(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -109,14 +118,14 @@ func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
}
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
if commandUsesContext(cmd.Name()) {
|
||||
if commandUsesContext(cmd.CommandPath()) {
|
||||
globalEulaPostRun(cmd, p)
|
||||
}
|
||||
},
|
||||
}
|
||||
origHelpFunc := cmd.HelpFunc()
|
||||
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
|
||||
if !commandUsesContext(cmd.Name()) {
|
||||
if !commandUsesContext(cmd.CommandPath()) {
|
||||
cmd.Flags().MarkHidden("acceptEULA")
|
||||
}
|
||||
origHelpFunc(cmd, args)
|
||||
@@ -163,9 +172,6 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
|
||||
//add upgrade command
|
||||
cmd.AddCommand(upgradeCmd(p))
|
||||
|
||||
// add the set-context config command as a sub-command to the app config command
|
||||
configCmd.AddCommand(setContextConfigCmd(p))
|
||||
|
||||
@@ -212,7 +218,7 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
preflightCmd.AddCommand(pfCreateRoleCheckCmd(p))
|
||||
preflightCmd.AddCommand(pfCreateRoleBindingCheckCmd(p))
|
||||
preflightCmd.AddCommand(pfCreateServiceAccountCheckCmd(p))
|
||||
preflightCmd.AddCommand(pfCreateRBCheckCmd(p))
|
||||
preflightCmd.AddCommand(pfCreateAuthCheckCmd(p))
|
||||
|
||||
cmd.AddCommand(preflightCmd)
|
||||
cmd.AddCommand(loadCrFile(p))
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func uninstallCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
skipConfirmation := false
|
||||
c := &cobra.Command{
|
||||
Use: "uninstall",
|
||||
Short: "Uninstall the deployed qliksense.",
|
||||
@@ -13,10 +14,15 @@ func uninstallCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
Example: `qliksense uninstall <context-name>`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
return q.UninstallQK8s(args[0])
|
||||
return q.UninstallQK8s(args[0], skipConfirmation)
|
||||
}
|
||||
return q.UninstallQK8s("")
|
||||
return q.UninstallQK8s("", skipConfirmation)
|
||||
},
|
||||
}
|
||||
|
||||
f := c.Flags()
|
||||
|
||||
f.BoolVar(&skipConfirmation, "yes", skipConfirmation, "skips confirmation")
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func upgradeCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
keepPatchFiles := false
|
||||
c := &cobra.Command{
|
||||
Use: "upgrade",
|
||||
Short: "upgrade qliksense release",
|
||||
Long: `upgrade qliksense release`,
|
||||
Example: `qliksense upgrade <version>`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return q.UpgradeQK8s(keepPatchFiles)
|
||||
},
|
||||
}
|
||||
|
||||
f := c.Flags()
|
||||
f.BoolVar(&keepPatchFiles, keepPatchFilesFlagName, keepPatchFiles, keepPatchFilesFlagUsage)
|
||||
return c
|
||||
}
|
||||
@@ -1,13 +1,47 @@
|
||||
# qliksense command reference
|
||||
# CLI reference
|
||||
|
||||
## qliksense apply
|
||||
### qliksense preflight
|
||||
|
||||
`qliksense apply` command takes input a cr file or input from pipe
|
||||
Preflight checks provide pre-installation cluster conformance testing and validation before we install qliksense on the cluster. We gather a suite of conformance tests that can be easily written and run on the target cluster to verify that cluster-specific requirements are met.
|
||||
|
||||
We support the following tests at the moment as part of preflight checks, and the range of the suite will be expanded in future.
|
||||
|
||||
Run the following command to view help about the commands supported by preflight at any moment:
|
||||
```
|
||||
qliksense preflight
|
||||
```
|
||||
|
||||
#### Running all checks
|
||||
Run the following command to execute all preflight checks
|
||||
```
|
||||
qliksense preflight all --mongodb-url=<mongo-server url> --mongodb-ca-cert=<path to ca-cert file>
|
||||
```
|
||||
|
||||
#### Running specific check
|
||||
Run the following command to execute a specific check
|
||||
```
|
||||
qliksense preflight dns
|
||||
```
|
||||
|
||||
### qliksense load
|
||||
|
||||
`qliksense load` command takes input from a file or from pipe
|
||||
|
||||
- `qliksense load -f cr-file.yaml`
|
||||
- `cat cr-file.yaml | qliksense load -f -`
|
||||
|
||||
This will load the Custom Resource (CR) into `${QLIKSENSE_HOME}` folder, create context structure and set the current context to that CR.
|
||||
|
||||
This will also encrypt the secrets from CR while writing the CR into the disk.
|
||||
|
||||
### qliksense apply
|
||||
|
||||
`qliksense apply` command takes input from a file or from pipe
|
||||
|
||||
- `qliksense apply -f cr-file.yaml`
|
||||
- `cat cr-file.yaml | qliksense apply -f -`
|
||||
|
||||
the content of `cr-file.yaml` should be something similar
|
||||
The content of `cr-file.yaml` should be something like the following:
|
||||
|
||||
```yaml
|
||||
apiVersion: qlik.com/v1
|
||||
@@ -27,33 +61,26 @@ spec:
|
||||
value: mongodb://qlik-test-mongodb:27017/qliksense?ssl=false
|
||||
profile: docker-desktop
|
||||
rotateKeys: "yes"
|
||||
```
|
||||
```
|
||||
|
||||
This will do everything `qliksense load` does and install the qliksense into the cluster.
|
||||
`qliksense apply` does everything `qliksense load` does but will install Qlik Sense into the cluster as well
|
||||
|
||||
## qliksense load
|
||||
### qliksense about
|
||||
|
||||
`qliksense load` command takes input a cr file or input from pipe.
|
||||
`qliksense about` command will display information about [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s) release.
|
||||
|
||||
- `qliksense load -f cr-file.yaml`
|
||||
- `cat cr-file.yaml | qliksense load -f -`
|
||||
For example, running the following command will show information about default profile for `1.0.0` tag
|
||||
|
||||
This will load the CR into `${QLIKSENSE_HOME}` folder, create context structure and set the current context to that CR.
|
||||
This will also encrypt the secrets from CR while writing the CR into the disk.
|
||||
```
|
||||
qliksense about 1.0.0
|
||||
```
|
||||
|
||||
## qliksense about
|
||||
Run the following command to view options for `about` command:
|
||||
```
|
||||
qliksense about --help
|
||||
```
|
||||
|
||||
About action will display inside information regarding [qliksense-k8](https://github.com/qlik-oss/qliksense-k8s) release.
|
||||
|
||||
it will support following flags
|
||||
|
||||
- `qliksense about 1.0.0` display default profile for tag `1.0.0`.
|
||||
- `qliksense about 1.0.0 --profile=docker-desktop`
|
||||
- `qliksense about`
|
||||
- assuming current directory has `manifests/docker-desktop`
|
||||
- or get version information from pull of `qliksense-k8s` `master`
|
||||
|
||||
using other supported commands user might have built the CR into the location `~/.qliksense/myqliksense.yaml`
|
||||
Using other supported commands user might have built the CR into the location `~/.qliksense/myqliksense.yaml`
|
||||
|
||||
```yaml
|
||||
apiVersion: qlik.com/v1
|
||||
@@ -62,7 +89,7 @@ metadata:
|
||||
name: myqliksense
|
||||
spec:
|
||||
profile: docker-desktop
|
||||
manifestsRoot: /Usr/ddd/my-k8-repo/manifests
|
||||
manifestsRoot: /Usr/xyz/my-k8-repo/manifests
|
||||
namespace: myqliksense
|
||||
storageClassName: efs
|
||||
configs:
|
||||
@@ -77,31 +104,30 @@ spec:
|
||||
valueFromKey: messagingPassword
|
||||
```
|
||||
|
||||
In that case the command would be
|
||||
In this case, the result of `qliksense about` command would display information from:
|
||||
|
||||
- `qliksense about`
|
||||
- display from `/Usr/ddd/my-k8-repo/manifests/docker-desktop` location
|
||||
- pull from `master` if directory invalid/empty
|
||||
- `/Usr/xyz/my-k8-repo/manifests/docker-desktop` location, or
|
||||
- Pull and show information from `master` branch if the directory is invalid or empty
|
||||
|
||||
|
||||
## qliksense config
|
||||
### qliksense config
|
||||
|
||||
Config action will perform operations on configurations and contexts regarding the [qliksense-k8](https://github.com/qlik-oss/qliksense-k8s) release.
|
||||
`qliksense config` will perform operations on configurations and contexts regarding the [qliksense-k8](https://github.com/qlik-oss/qliksense-k8s) release.
|
||||
|
||||
it will support following commands:
|
||||
It supports the following flags:
|
||||
|
||||
- `qliksense config apply` - generate the patchs and apply manifests to k8s
|
||||
- `qliksense config list-contexts` - retrieves the contexts and lists them
|
||||
- `qliksense config set` - configure a key value pair into the current context
|
||||
- `qliksense config set-configs` - set configurations into the qliksense context as key-value pairs
|
||||
- `qliksense config set-context` - sets the context in which the Kubernetes cluster and resources live in
|
||||
- `qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=false` - set secrets configurations into the qliksense context as key-value pairs and show encrypted value as part of CR
|
||||
- `qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=true` - set secrets configurations into the qliksense context as key-value pairs and show a key reference to the created Kubernetes secret resource as part of the CR
|
||||
- `qliksense config apply` - generate the patches and apply manifests to K8s
|
||||
- `qliksense config list-contexts` - get and list contexts
|
||||
- `qliksense config set` - configure a key-value pair into the current context
|
||||
- `qliksense config set-configs` - set configurations into qliksense context as key-value pairs
|
||||
- `qliksense config set-context` - sets the Kubernetes context where resources are located
|
||||
- `qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=false` - set secrets configurations into qliksense context as key-value pairs and show encrypted value as part of CR
|
||||
- `qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=true` - set secrets configurations into qliksense context as key-value pairs and show a key reference to the created Kubernetes secret resource as part of the CR
|
||||
- `qliksense config view` - view the qliksense operator CR
|
||||
- `qliksense config delete-context` - deletes a specific context locally (not in-cluster). Deletes context in spec of `config.yaml` and locally deletes entire folder of specified context (does not delete in-cluster secrets)
|
||||
- `qliksense config delete-context` - deletes a specific context locally (not in-cluster). Deletes context in spec of `config.yaml` and locally deletes entire folder of specified context (does not delete secrets from cluster)
|
||||
|
||||
|
||||
the global file that abstracts all the contexts is `config.yaml`, located at: `~/.qliksense/config.yaml`:
|
||||
The global file which abstracts all contexts is `~/.qliksense/config.yaml`
|
||||
```yaml
|
||||
apiVersion: config.qlik.com/v1
|
||||
kind: QliksenseConfig
|
||||
@@ -110,10 +136,10 @@ metadata:
|
||||
spec:
|
||||
contexts:
|
||||
- name: qlik-default
|
||||
crFile: /Users/fff/.qliksense/contexts/qlik-default/qlik-default.yaml
|
||||
crFile: /Users/xyz/.qliksense/contexts/qlik-default/qlik-default.yaml
|
||||
- name: myqliksense
|
||||
crFile: /Users/fff/.qliksense/contexts/myqliksense/myqliksense.yaml
|
||||
crFile: /Users/xyz/.qliksense/contexts/myqliksense/myqliksense.yaml
|
||||
- name: hello
|
||||
crFile: /Users/fff/.qliksense/contexts/hello/hello.yaml
|
||||
crFile: /Users/xyz/.qliksense/contexts/hello/hello.yaml
|
||||
currentContext: hello
|
||||
```
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# How qliksense cli works
|
||||
# How CLI works
|
||||
|
||||
At the initialization, `qliksense` cli creates few files in the director `~/.qliksene` and it contains following files:
|
||||
|
||||
@@ -48,14 +48,14 @@ qliksense config -h
|
||||
|
||||
In this mode `qliksense` CLI downloads the specified version from [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s) and places it in `~/.qliksense/contexts/<context-name>/qlik-k8s` folder.
|
||||
|
||||
The qliksense cli creates a CR for the QlikSense operator and all config operations are peformed to edit the CR.
|
||||
The qliksense cli creates a CR for the QlikSense operator and all config operations are performed to edit the CR.
|
||||
|
||||
`qliksense install` or `qliksense config apply` will generate patches in local file system (i.e `~/.qliksense/contexts/<context-name>/qlik-k8s`) and
|
||||
|
||||
- Install those manifests into the cluster
|
||||
- Create a custom resoruce (CR) for the `qliksene operator`.
|
||||
- Create a custom resource (CR) for the `qliksene operator`.
|
||||
|
||||
The operator makes the association to the installed resoruces so that when `qliksense uninstall` is performed the operator can delete all kubernetes resources related to QSEoK for the current context.
|
||||
The operator makes the association to the installed resources so that when `qliksense uninstall` is performed the operator can delete all kubernetes resources related to QSEoK for the current context.
|
||||
|
||||
## With a git repo
|
||||
|
||||
@@ -94,4 +94,3 @@ spec:
|
||||
image: qlik-docker-oss.bintray.io/qliksense-repo-watcher
|
||||
....
|
||||
```
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
- 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_
|
||||
|
||||
## Installing Sense installer
|
||||
## 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`
|
||||
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
# Overview
|
||||
|
||||
The Qlik Sense installer 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). This cli facilitates:
|
||||
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:
|
||||
|
||||
- Installation of QSEoK
|
||||
- Installation of qliksense operator to manage QSEoK
|
||||
- Air gapped installation of QSEoK
|
||||
|
||||
!!! 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
|
||||
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
|
||||
|
||||
## Future Direction
|
||||
|
||||
Operations:
|
||||
|
||||
- Expand preflight checks
|
||||
- Backup/restore operations
|
||||
- Fully support airgap installation of QSEoK
|
||||
- Restore unwanted deletion of kubernetes resources
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
##Preflight checks
|
||||
# Preflight checks
|
||||
|
||||
Preflight checks provide pre-installation cluster conformance testing and validation before we install qliksense on the cluster. We gather a suite of conformance tests that can be easily written and run on the target cluster to verify that cluster-specific requirements are met.
|
||||
The suite consists of a set of `collectors` which run the specifications of every test and `analyzers` which analyze the results of every test run by the collector.
|
||||
|
||||
We support the following tests at the moment as part of preflight checks, and the range of the suite will be expanded in future.
|
||||
|
||||
Run the following command to view help about the commands supported by preflight at any moment:
|
||||
```console
|
||||
```shell
|
||||
$ qliksense preflight
|
||||
perform preflight checks on the cluster
|
||||
|
||||
@@ -26,7 +27,7 @@ Flags:
|
||||
### DNS check
|
||||
Run the following command to perform preflight DNS check. We setup a kubernetes deployment and try to reach it as part of establishing DNS connectivity in this check.
|
||||
The expected output should be similar to the one shown below.
|
||||
```console
|
||||
```shell
|
||||
$ qliksense preflight dns
|
||||
|
||||
Preflight DNS check
|
||||
@@ -49,7 +50,7 @@ Deleted deployment: dep-dns-preflight-check
|
||||
### Kubernetes version check
|
||||
We check the version of the target kubernetes cluster and ensure that it falls in the valid range of kubernetes versions that are supported by qliksense.
|
||||
The command to run this check and the expected similar output are as shown below:
|
||||
```console
|
||||
```shell
|
||||
$ qliksense preflight k8s-version
|
||||
|
||||
Preflight kubernetes minimum version check
|
||||
@@ -64,7 +65,7 @@ Completed Preflight kubernetes minimum version check
|
||||
|
||||
### Service check
|
||||
We use the commmand below to test if we are able to create a service in the cluster.
|
||||
```console
|
||||
```shell
|
||||
$ qliksense preflight service
|
||||
|
||||
Preflight service check
|
||||
@@ -80,7 +81,7 @@ Completed preflight service check
|
||||
|
||||
### Deployment check
|
||||
We use the commmand below to test if we are able to create a deployment in the cluster. After the test exexutes, we wait until the created deployment terminates before we exit the command.
|
||||
```console
|
||||
```shell
|
||||
$ qliksense preflight deployment
|
||||
|
||||
Preflight deployment check
|
||||
@@ -95,7 +96,7 @@ Completed preflight deployment check
|
||||
|
||||
### Pod check
|
||||
We use the commmand below to test if we are able to create a pod in the cluster.
|
||||
```console
|
||||
```shell
|
||||
$ qliksense preflight pod
|
||||
|
||||
Preflight pod check
|
||||
@@ -197,6 +198,8 @@ We can check if we are able to connect to an instance of mongodb on the cluster
|
||||
```shell
|
||||
qliksense preflight mongo --url=<url> OR
|
||||
qliksense preflight mongo
|
||||
qliksense preflight mongo --url=<mongo-server url> --ca-cert=<path to ca-cert file>
|
||||
|
||||
|
||||
Preflight mongo check
|
||||
---------------------
|
||||
@@ -204,7 +207,7 @@ Preflight mongodb check:
|
||||
Created pod: pf-mongo-pod
|
||||
stdout: MongoDB shell version v4.2.5
|
||||
connecting to: <url>/?compressors=disabled&gssapiServiceName=mongodb
|
||||
Implicit session: session { "id" : UUID("64f639d3-2c93-4894-80f6-ee14acaf56a5") }
|
||||
Implicit session: session { "id" : UUID("...") }
|
||||
MongoDB server version: 4.2.5
|
||||
bye
|
||||
stderr:
|
||||
@@ -217,9 +220,9 @@ Completed preflight mongodb check
|
||||
|
||||
### Running all checks
|
||||
Run the command shown below to execute all preflight checks.
|
||||
```console
|
||||
```shell
|
||||
$ qliksense preflight all --mongodb-url=<url> OR
|
||||
$ qliksense preflight all
|
||||
$ qliksense preflight all --mongodb-url=<mongo-server url> --mongodb-ca-cert=<path to ca-cert file>
|
||||
|
||||
Running all preflight checks
|
||||
|
||||
|
||||
9
go.mod
9
go.mod
@@ -26,23 +26,25 @@ require (
|
||||
github.com/docker/cli v0.0.0-20191212191748-ebca1413117a // indirect
|
||||
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/go-git/go-git/v5 v5.0.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
|
||||
github.com/gobuffalo/packr/v2 v2.7.1
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||
github.com/golang/protobuf v1.3.3 // indirect
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/mux v1.7.3 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||
github.com/kyokomi/emoji v2.2.2+incompatible
|
||||
github.com/mattn/go-colorable v0.1.4
|
||||
github.com/mattn/go-tty v0.0.3
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/otiai10/copy v1.1.1
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/qlik-oss/k-apis v0.0.34
|
||||
github.com/qlik-oss/k-apis v0.1.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
||||
github.com/spf13/cobra v0.0.6
|
||||
@@ -50,8 +52,7 @@ require (
|
||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31
|
||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 // indirect
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a // indirect
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
||||
golang.org/x/tools v0.0.0-20200312194400-c312e98713c2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b // indirect
|
||||
google.golang.org/grpc v1.27.0 // indirect
|
||||
|
||||
36
go.sum
36
go.sum
@@ -207,6 +207,7 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -299,6 +300,12 @@ github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aev
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
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-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=
|
||||
@@ -667,6 +674,10 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kyokomi/emoji v1.5.1 h1:qp9dub1mW7C4MlvoRENH6EAENb9skEFOvIEbp1Waj38=
|
||||
github.com/kyokomi/emoji v2.2.2+incompatible h1:gaQFbK2+uSxOR4iGZprJAbpmtqTrHhSdgOyIMD6Oidc=
|
||||
github.com/kyokomi/emoji v2.2.2+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
@@ -760,6 +771,7 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
|
||||
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
@@ -792,6 +804,14 @@ github.com/opencontainers/selinux v1.3.0 h1:xsI95WzPZu5exzA6JzkLSfdr/DilzOhCJOqG
|
||||
github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
|
||||
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw=
|
||||
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc=
|
||||
github.com/otiai10/copy v1.1.1 h1:PH7IFlRQ6Fv9vYmuXbDRLdgTHoP1w483kPNUP2bskpo=
|
||||
github.com/otiai10/copy v1.1.1/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI=
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc=
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
@@ -852,10 +872,12 @@ 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.0.34 h1:lOC21wz/nNZNmSfTXZSJCOm1BulaZfdg7tAuYb7knAE=
|
||||
github.com/qlik-oss/k-apis v0.0.34/go.mod h1:DNiWYqCqPIN216l7+1rccArNIYPb1Le7kYDcPSyNp+Q=
|
||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200401055330-fa528324112a h1:Vzod5XB+e25ENy5Lse0pXNmSYSDFxSEYhH/6Sj7twPg=
|
||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200401055330-fa528324112a/go.mod h1:tSQaDZ4Jt9KwYvD7LlMUPi5nkiGOno3PAKl5/XqEfxs=
|
||||
github.com/qlik-oss/k-apis v0.0.39 h1:fIGCC7f9kU7319VTSJKr3fLoA9E4MjusRFmOjX3ypis=
|
||||
github.com/qlik-oss/k-apis v0.0.39/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
|
||||
github.com/qlik-oss/k-apis v0.1.0 h1:uMl1316SNYy5Hm6jy1U7wiCMkut0tKqdP8mBpSuXXp8=
|
||||
github.com/qlik-oss/k-apis v0.1.0/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
|
||||
github.com/qlik-oss/k-apis v0.1.1 h1:aZ4eTMB3mSn03Kuj7+RI0eFLkjK9+0qxADBioRb3qVA=
|
||||
github.com/qlik-oss/k-apis v0.1.1/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
|
||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200402170547-2e8140160c36 h1:BuT+cnXPQ6mcOWTDS1S8GXy65LAEMdPuNQCC36rMq28=
|
||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200402170547-2e8140160c36/go.mod h1:tSQaDZ4Jt9KwYvD7LlMUPi5nkiGOno3PAKl5/XqEfxs=
|
||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||
@@ -941,6 +963,8 @@ github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
|
||||
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/src-d/go-git v4.7.0+incompatible h1:IYSSnbAHeKmsfbQFi9ozbid+KNh0bKjlorMfQehQbcE=
|
||||
github.com/src-d/go-git v4.7.0+incompatible/go.mod h1:1bQciz+hn0jzPQNsYj0hDFZHLJBdV7gXE2mWhC7EkFk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
@@ -1056,6 +1080,7 @@ golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U=
|
||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA=
|
||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1136,6 +1161,8 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjut
|
||||
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=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1299,6 +1326,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
site_name: Qlik Sense installer
|
||||
site_name: Qlik Sense on Kubernetes CLI
|
||||
repo_url: 'https://github.com/qlik-oss/sense-installer'
|
||||
strict: true
|
||||
theme:
|
||||
@@ -19,6 +19,5 @@ nav:
|
||||
- getting_started.md
|
||||
- command_reference.md
|
||||
- concepts.md
|
||||
- preflight_checks.md
|
||||
- air_gap.md
|
||||
- Releases ⧉: https://github.com/qlik-oss/sense-installer/releases
|
||||
|
||||
153
pkg/api/apis.go
153
pkg/api/apis.go
@@ -1,11 +1,9 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -23,6 +21,7 @@ const (
|
||||
qliksenseContextsDirName = "contexts"
|
||||
qliksenseSecretsDirName = "secrets"
|
||||
qliksenseEjsonDirName = "ejson"
|
||||
QLIK_GIT_REPO = "https://github.com/qlik-oss/qliksense-k8s"
|
||||
)
|
||||
|
||||
// NewQConfig create QliksenseConfig object from file ~/.qliksense/config.yaml
|
||||
@@ -144,6 +143,66 @@ func (cr *QliksenseCR) IsRepoExist() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) GetFetchUrl() string {
|
||||
if cr.Spec.FetchSource == nil || cr.Spec.FetchSource.Repository == "" {
|
||||
return QLIK_GIT_REPO
|
||||
}
|
||||
return cr.Spec.FetchSource.Repository
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) GetFetchAccessToken(encryptionKey string) string {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
return ""
|
||||
}
|
||||
if tok, err := cr.Spec.FetchSource.GetAccessToken(); err != nil {
|
||||
fmt.Println(err)
|
||||
return ""
|
||||
} else {
|
||||
by, _ := b64.StdEncoding.DecodeString(tok)
|
||||
res, err := DecryptData(by, encryptionKey)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return ""
|
||||
}
|
||||
return string(res)
|
||||
}
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) SetFetchUrl(url string) {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
cr.Spec.FetchSource = &config.Repo{}
|
||||
}
|
||||
cr.Spec.FetchSource.Repository = url
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) SetFetchAccessToken(token, encryptionKey string) error {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
cr.Spec.FetchSource = &config.Repo{}
|
||||
}
|
||||
res, err := EncryptData([]byte(token), encryptionKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cr.Spec.FetchSource.AccessToken = b64.StdEncoding.EncodeToString(res)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) SetFetchAccessSecretName(sec string) {
|
||||
if cr.Spec.FetchSource == nil {
|
||||
cr.Spec.FetchSource = &config.Repo{}
|
||||
}
|
||||
cr.Spec.FetchSource.SecretName = sec
|
||||
}
|
||||
|
||||
//DeleteRepo delete the manifest repo and unset manifestsRoot
|
||||
func (cr *QliksenseCR) DeleteRepo() error {
|
||||
if err := os.RemoveAll(cr.Spec.ManifestsRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
cr.Spec.ManifestsRoot = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) IsRepoExist(contextName, version string) bool {
|
||||
if _, err := os.Lstat(qc.BuildRepoPathForContext(contextName, version)); err != nil {
|
||||
return false
|
||||
@@ -158,6 +217,11 @@ func (qc *QliksenseConfig) IsRepoExistForCurrent(version string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) DeleteRepoForCurrent(version string) error {
|
||||
path := qc.BuildRepoPath(version)
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) BuildRepoPath(version string) string {
|
||||
return qc.BuildRepoPathForContext(qc.Spec.CurrentContext, version)
|
||||
}
|
||||
@@ -258,9 +322,9 @@ func (qc *QliksenseConfig) GetCurrentContextSecretsDir() (string, error) {
|
||||
func (qc *QliksenseConfig) setDockerConfigJsonSecret(filename string, dockerConfigJsonSecret *DockerConfigJsonSecret) error {
|
||||
if secretsDir, err := qc.GetCurrentContextSecretsDir(); err != nil {
|
||||
return err
|
||||
} else if publicKey, _, err := qc.GetCurrentContextEncryptionKeyPair(); err != nil {
|
||||
} else if encryptionKey, err := qc.GetEncryptionKeyForCurrent(); err != nil {
|
||||
return err
|
||||
} else if dockerConfigJsonSecretYaml, err := dockerConfigJsonSecret.ToYaml(publicKey); err != nil {
|
||||
} else if dockerConfigJsonSecretYaml, err := dockerConfigJsonSecret.ToYaml(encryptionKey); err != nil {
|
||||
return err
|
||||
} else if err := os.MkdirAll(secretsDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
@@ -307,9 +371,9 @@ func (qc *QliksenseConfig) getDockerConfigJsonSecret(name string) (*DockerConfig
|
||||
return nil, err
|
||||
} else if dockerConfigJsonSecretYaml, err := ioutil.ReadFile(filepath.Join(secretsDir, name)); err != nil {
|
||||
return nil, err
|
||||
} else if _, privateKey, err := qc.GetCurrentContextEncryptionKeyPair(); err != nil {
|
||||
} else if encryptionKey, err := qc.GetEncryptionKeyForCurrent(); err != nil {
|
||||
return nil, err
|
||||
} else if err := dockerConfigJsonSecret.FromYaml(dockerConfigJsonSecretYaml, privateKey); err != nil {
|
||||
} else if err := dockerConfigJsonSecret.FromYaml(dockerConfigJsonSecretYaml, encryptionKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dockerConfigJsonSecret, nil
|
||||
@@ -320,11 +384,11 @@ func (qc *QliksenseConfig) getCurrentContextEncryptionKeyPairLocation() (string,
|
||||
if qcr, err := qc.GetCurrentCR(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return qc.getContextEncryptionKeyPairLocation(qcr.GetName())
|
||||
return qc.getContextEncryptionKeyLocation(qcr.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) getContextEncryptionKeyPairLocation(contextName string) (string, error) {
|
||||
func (qc *QliksenseConfig) getContextEncryptionKeyLocation(contextName string) (string, error) {
|
||||
// Check env var: QLIKSENSE_KEY_LOCATION to determine location to store keypair
|
||||
var secretKeyPairLocation string
|
||||
if os.Getenv("QLIKSENSE_KEY_LOCATION") != "" {
|
||||
@@ -334,9 +398,9 @@ func (qc *QliksenseConfig) getContextEncryptionKeyPairLocation(contextName strin
|
||||
// QLIKSENSE_KEY_LOCATION has not been set, hence storing key pair in default location:
|
||||
// /.qliksense/secrets/contexts/<current-context>/secrets/
|
||||
secretKeyPairLocation = filepath.Join(qc.QliksenseHomePath, qliksenseSecretsDirName, qliksenseContextsDirName, contextName, qliksenseSecretsDirName)
|
||||
|
||||
}
|
||||
return secretKeyPairLocation, nil
|
||||
|
||||
return secretKeyPairLocation, os.MkdirAll(secretKeyPairLocation, os.ModePerm)
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) GetCurrentContextEjsonKeyDir() (string, error) {
|
||||
@@ -351,52 +415,25 @@ func (qc *QliksenseConfig) GetCurrentContextEjsonKeyDir() (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) GetCurrentContextEncryptionKeyPair() (*rsa.PublicKey, *rsa.PrivateKey, error) {
|
||||
func (qc *QliksenseConfig) GetEncryptionKeyForCurrent() (string, error) {
|
||||
if qcr, err := qc.GetCurrentCR(); err != nil {
|
||||
return nil, nil, err
|
||||
return "", err
|
||||
} else {
|
||||
return qc.GetContextEncryptionKeyPair(qcr.GetName())
|
||||
return qc.GetEncryptionKeyFor(qcr.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
func (qc *QliksenseConfig) GetContextEncryptionKeyPair(contextName string) (*rsa.PublicKey, *rsa.PrivateKey, error) {
|
||||
secretKeyPairLocation, err := qc.getContextEncryptionKeyPairLocation(contextName)
|
||||
func (qc *QliksenseConfig) GetEncryptionKeyFor(contextName string) (string, error) {
|
||||
secretKeyLocation, err := qc.getContextEncryptionKeyLocation(contextName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
publicKeyFilePath := filepath.Join(secretKeyPairLocation, QliksensePublicKey)
|
||||
privateKeyFilePath := filepath.Join(secretKeyPairLocation, QliksensePrivateKey)
|
||||
// try to create the dir if it doesn't exist
|
||||
if !FileExists(publicKeyFilePath) || !FileExists(privateKeyFilePath) {
|
||||
LogDebugMessage("Qliksense secretKeyLocation dir does not exist, creating it now: %s", secretKeyPairLocation)
|
||||
if err := os.MkdirAll(secretKeyPairLocation, os.ModePerm); err != nil {
|
||||
err = fmt.Errorf("Not able to create %s dir: %v", secretKeyPairLocation, err)
|
||||
log.Println(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
// generating and storing key-pair
|
||||
err1 := GenerateAndStoreSecretKeypair(secretKeyPairLocation)
|
||||
if err1 != nil {
|
||||
err1 = fmt.Errorf("Not able to generate and store key pair for encryption")
|
||||
log.Println(err1)
|
||||
return nil, nil, err1
|
||||
}
|
||||
}
|
||||
|
||||
if publicKeyBytes, err := ReadKeys(publicKeyFilePath); err != nil {
|
||||
LogDebugMessage("Not able to read public key")
|
||||
return nil, nil, err
|
||||
} else if privateKeyBytes, err := ReadKeys(privateKeyFilePath); err != nil {
|
||||
LogDebugMessage("Not able to read private key")
|
||||
return nil, nil, err
|
||||
} else if rsaPublicKey, err := DecodeToPublicKey(publicKeyBytes); err != nil {
|
||||
return nil, nil, err
|
||||
} else if rsaPrivateKey, err := DecodeToPrivateKey(privateKeyBytes); err != nil {
|
||||
return nil, nil, err
|
||||
} else {
|
||||
return rsaPublicKey, rsaPrivateKey, nil
|
||||
key, err := LoadSecretKey(secretKeyLocation)
|
||||
if key != "" {
|
||||
return key, nil
|
||||
}
|
||||
fmt.Println("Generating new encryption key for the context: " + contextName)
|
||||
return GenerateAndStoreSecretKey(secretKeyLocation)
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) AddLabelToCr(key, value string) {
|
||||
@@ -421,17 +458,6 @@ func (cr *QliksenseCR) GetString() (string, error) {
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) GetImageRegistry() string {
|
||||
for _, nameValues := range cr.Spec.Configs {
|
||||
for _, nameValue := range nameValues {
|
||||
if nameValue.Name == "imageRegistry" {
|
||||
return nameValue.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) GetK8sSecretsFolder(qlikSenseHomeDir string) string {
|
||||
return filepath.Join(qlikSenseHomeDir, qliksenseContextsDirName, cr.GetName(), qliksenseSecretsDirName)
|
||||
}
|
||||
@@ -469,7 +495,7 @@ func (cr *QliksenseCR) GetCustomCrdsPath() string {
|
||||
func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error) {
|
||||
newCr := &QliksenseCR{}
|
||||
copier.Copy(newCr, cr)
|
||||
_, rsaPrivateKey, err := qc.GetCurrentContextEncryptionKeyPair()
|
||||
encryptionKey, err := qc.GetEncryptionKeyFor(cr.GetName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -482,7 +508,7 @@ func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db, err := Decrypt(b, rsaPrivateKey)
|
||||
db, err := DecryptData(b, encryptionKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -495,6 +521,11 @@ func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error)
|
||||
finalSecrets[k] = newNvs
|
||||
}
|
||||
newCr.Spec.Secrets = finalSecrets
|
||||
|
||||
if newCr.Spec.FetchSource != nil && newCr.Spec.FetchSource.AccessToken != "" {
|
||||
decData := cr.GetFetchAccessToken(encryptionKey)
|
||||
newCr.Spec.FetchSource.AccessToken = decData
|
||||
}
|
||||
return newCr, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
b64 "encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@@ -52,12 +53,6 @@ spec:
|
||||
qliksense:
|
||||
- name: acceptEULA
|
||||
value: "yes"
|
||||
secrets:
|
||||
qliksense:
|
||||
- name: mongoDbUri
|
||||
# this is rsa encrypted value, the pub and pri keys are in setuPublicAndPrivateKey() method
|
||||
# actual value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||
value: n/pDi7Z/A3i16cAHFFwMp19/egNKc8WZxm6MKHLT/B1DMv3U6pDXWyXT5fYYDV1wDTO3Vk43yECST1UgZYmMpgUOwgSfGgqTVi2VqS0JQsnwI+Twwhnvha8RJANX8b/XIoSFVWaOgy7+RP35ZkvOqHdCfC2aT8JMIHgBQqqCbsNgimCuRSxi0klR000ic/Tp5PYSz5mD+WLrkPw2FbS0OVBsQ/hIp5GZrmVpvEOZdbT63Sz+n/G4Br6GTv2LkZcU7JBuKQm2wfB+mRjJmJnNrPawLfn2UZ89Rz0BLwIy+6b24/RoIUgoNowfGkJreGiwItGK8fjCcx11oavK/yAo6pYZXCcru46pmHbxxle1OlkdTKkG6EVtJuKjSZXtVmBHZYRFzsR7HnAiXnL7QzSEcS7ieZlQvTmNLfpidJhK199oSbyKREqXGl2S8DzPKM9RLccVbQTy6X8qWimP3MYCnO4K0KoQnNQAgfuV8ZxnvdDecByLDPIpmFMGy0Xm9pUZWxmSoDBq+p5WBI2HdCX2gCYVv5yxS2iBqO5SMKo8iOglHtPI9NIMvloERdN1vZtxSRkY5uDEfrU9ysYwfayEXxvXmdWv0HxlotcgUinP02j7k+OfIapTmY/jGfvF4euyCGRKuJ9JlSD9pIiRdAcekjL6hCxXLJLdajCV4sL/YDo=
|
||||
`
|
||||
ctx1Dir := filepath.Join(homeDir, "contexts", "contx1")
|
||||
crFile := filepath.Join(ctx1Dir, "contx1.yaml")
|
||||
@@ -106,13 +101,16 @@ func TestGetDecryptedCr(t *testing.T) {
|
||||
t.Fail()
|
||||
t.Log(e)
|
||||
}
|
||||
qcr, err := qct.GetCurrentCR()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
setuPublicAndPrivateKey(dir)
|
||||
qcr, err := qct.GetCurrentCR()
|
||||
|
||||
key, _ := setupGenerateKey(dir)
|
||||
ecn, _ := EncryptData([]byte("mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"), key)
|
||||
b := b64.StdEncoding.EncodeToString(ecn)
|
||||
qcr.Spec.AddToSecrets("qliksense", "mongoDbUri", b, "")
|
||||
|
||||
qcr.SetFetchAccessToken("mytoken", key)
|
||||
|
||||
newCr, err := qct.GetDecryptedCr(qcr)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
@@ -129,77 +127,12 @@ func TestGetDecryptedCr(t *testing.T) {
|
||||
if decryptedValue == orignalValue {
|
||||
t.Fail()
|
||||
}
|
||||
if newCr.Spec.FetchSource.AccessToken != "mytoken" {
|
||||
t.Fail()
|
||||
}
|
||||
td()
|
||||
}
|
||||
func setuPublicAndPrivateKey(homeDir string) ([]byte, []byte, error) {
|
||||
privKeyBytes := []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJJwIBAAKCAgEAwUCimKCidbF3UxEHPy8K+hvhklRB9JYhj5sJy0if4lTVibkK
|
||||
1MrYCykOnmC40pPU9GLY1b8HxAg9tvyRn0YHUxOra6vVQaVcOVJhTM8D18d+lSr3
|
||||
Lp1yiX+UGT4nzWI9+R1CCbwXrqeQVoZs6QZKynEXMkFI9/wNMOwPOvQFOSTuoEoC
|
||||
O+zyTyUWEkNbUq825ELUQdIsjgmlWUOONudxsAr7ESRXW9QTHVh6uWmr3VRKZHby
|
||||
1JdU3I/wjdlGg5M2dDuXy5nQO9w/nYLjJXiw+zzOetZ/+t7/VOkOpNTeJQhwTM1W
|
||||
F7Y2VLetbi9FHgyzHatrduh07+XEiTbgDf3GIx2bp2p6oh0G3N2zpiLcK/aZj8ro
|
||||
uWWydfFfsU3MZ4FfJDP8I6b9awxjmKYqIr6hiPQCJaLBED8mwK+I5evIbnKv6E6u
|
||||
K+BApWA/R7ElragoFYbqQ1VpvntVMtJt9Dy5ZrI+IQARdXD3bb34oh0IPBhClnvv
|
||||
MUc1cWxDoXEX6oJ4I+LzxE87Zkwnan9qOwengolMVKFwPx1o37qrbmrXID21kKt7
|
||||
FL6xN4HxHLkItr1fKzdyWDFRHgASTAWfx5BIwvPuUW0vZHkvO80VyV2L63whVhPn
|
||||
PASmFkbviomrBttYfpr2aGQqF/qR1Nlxe834MFxk1pS9LMa/WnzvFr0gWakCAwEA
|
||||
AQKCAgARSp9B2N2wejibDiL/3E23I1eDqFZedDB8kPrHXbAwqDaTJCN79spt9TaB
|
||||
pVXkQaYEV/Pe7EDdoX8kKGU/QxzUqiXkdHOYdBtUZbKfFMbbP9ZrsnR7j0r4UpoF
|
||||
yDH3hprU93E5PcNAtW2M0GpeT1nR01yn+n908PCdOAIE3GC7RDq1zOl2QzVLL55R
|
||||
9ATv2Q2oTvJ/ETc7XlGVMx4+e2cIwXLFjeLjLI6pSYlxnarrGuetJZeEviWxto9n
|
||||
odFVZI6yx8JFTXX8ZTCr/1IjwDDVyhMPmrHI2Lsv9cqBpSpbVe32cUkKxhsGaYjz
|
||||
GvesQKamOPhco2ATNxPm0yopFlPsGKMfVl0BK0J6BqFh1BvU/SYJmXfnFuUNO3vV
|
||||
4u2Saa0q1iddxV0rXDwIqUfn+S6rwzK0G7y8bH2yvpB2VwiG3TFPnULep4wsefNq
|
||||
Fj92kqFBjacGpQLEEslUY0CMgeZ2+NuBQSUTscP3wBRsottMR6YXJtINdvfHBx+e
|
||||
EcN71z8D00w3mYqIQ7qb4Ml6HOqknunn58g43L9sACMUMTlEBXa9pUnScNYgWBAz
|
||||
W2q2mH37cIydM2JRZPpA8B4yTHt5ugJmChwyNFM7941arjKrebH+6AzLkofGedOP
|
||||
zg+vZQuPEXWs+3MBBnkWoyJW3Y0fbQdjsuQTtnd+7iyoxoBroQKCAQEA4dIiFlIS
|
||||
MDfRhQQWSiDvaw9aneDEJ3uo63ZRH5tm/IynLgtjYgEm/ZxlBCQgqRKLYELBxhu8
|
||||
SaF0uPK8pmpFJt0mIwSlsdeVhuE2obQeKUCczaqrKeaHS3PdWLjTlwph81BGRkHy
|
||||
qfqtNylyyMxrdEbnR51EtsWgFq6anTUAui1Q09JMuMNZRMOzDs1F4gExgD22rc0V
|
||||
c9YQ+jHJRxBGtNKMpMEqc8cvaxBidbItrN9SMTSWog7uYPBuEuaJ6K9vpgyJMOzJ
|
||||
SYcQEFGqgIqIDCg+ABE4d/4YROMKZ1DV/bJCind9brUHSx6XALsF0nC5c1Q9TnUL
|
||||
qI2khOwts4KYKwKCAQEA2xRC6Az97Vkdzu7BjLJ1FKmx4S2nEEgVS12ds82U+5Xf
|
||||
BHKAJnjqlqmmpzzJG+d77IYktz0+mey1QCNkqlm2fhuKs8LZMnpZRf0l8VcoBsUP
|
||||
/xKz7wfiE7RRFZtLJhPp4hhe43GzX5/JFMWMnC6UykwQbj4t1E/GNM/Suqwvg12M
|
||||
wktAJ6nqLgfhjQSO4xWo+nPzcbX+fNtrPCZVrBhYXihhcwRRNImWUCGJ6J4LMdPY
|
||||
Y9Z59qhOvE9cReH/Xw1av46omyiSyAqlgPyZ/kzA2IJSqYCjiQR/2+RD/g13jpcJ
|
||||
jatXLVZ8MJSL5OTS40G/HHTNNpNHbKKh0GOyxBA3ewKCAQBAn8UXhCcmW2L/YPsL
|
||||
/b7mcX9qPP+FmRLvR23R0MQ5M/tH5wRq8I969n3GIJykJeVzB8eybQ+GNslTgEvS
|
||||
iAkAJTubu+G7MkndTqg2wHf9MDtvdA8Fr646Po8yq7oJuHPtkKR7yLWsRUu6xIbP
|
||||
xgheP0hCq1QVxhqZQyCGKrvpi7xc0gsYuPbcAfFFJCOCmPrUi1SzCkTAYJt9LjA+
|
||||
wP6rErIjGBCRD4iXaBn1OqdtmH9KC5WsDP/VCBlIGWeQCly2NVIxiSHVg+xp7yUP
|
||||
IhXq/L05gbQaSsIhPKQmivCiaJg4The8TdwneDqYf+0bmxzHT203/bD3bImPbJNr
|
||||
ksz/AoIBAEwu4Y1cZzkQUmNRd5D7xecnk6ngfEYXKwCIT3zlMrfCSEl9n77BMaKu
|
||||
4Dsr0iuX9eosQ7xM2eYhAG6LYEg05lc4MKWOToVVMpI6E+W3Dz47bPKgiF3I+f8s
|
||||
Jz5CQIG/TwfGvciOE3hfUkec4ua09BzdEqGjkcBQ9XYMBxXPJr6h2379OBQS7FKR
|
||||
fwfQ2/dv4tElXTTfut2kV8gU9Jnh5Wjo1epvR+XjKpg28YQo4W+0YX1magcyRB8L
|
||||
4eSTUIC3XiVa8Jr0IwbZXPBb5xkdi7o+p4w2JahSHjxTRqmj+T1mnHXdbXVgq9Mg
|
||||
9Pzl7cgFZvX4UBx4XtASRf73jITNtt0CggEADH9K+O7FrIOSQly0sMvsRCMtejp3
|
||||
o+MDh1Q+vEg2kEgNXjS4ZFVljUpM2kg1OdUz7feS4dLXUJiIQ8ZWtZPedcq7wjHd
|
||||
02he5+s06l0jPifN3tX1ADfXGpXg5R2fbkrIzakkPP5/RO/aDxIUo7qhklNsVTXO
|
||||
VlGGfWLdk0ekA4upKm02Q1+YOlbIcAicEYYY8K7IffUwnohzKwL9yfuGi1VKTXpE
|
||||
4fzdegsHI03FSqR7V+LvtBpIupQ7RO4kuBmCEyI4E9FVknchg4te4gO3qwd9y0rJ
|
||||
Gu7HNIOrwOHzviI7J6Nd/l9MmeKqklHSgJvko/f5TmiXuQQ8xDZf84rcjQ==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`)
|
||||
|
||||
publicKeyBytes := []byte(`-----BEGIN RSA PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwUCimKCidbF3UxEHPy8K
|
||||
+hvhklRB9JYhj5sJy0if4lTVibkK1MrYCykOnmC40pPU9GLY1b8HxAg9tvyRn0YH
|
||||
UxOra6vVQaVcOVJhTM8D18d+lSr3Lp1yiX+UGT4nzWI9+R1CCbwXrqeQVoZs6QZK
|
||||
ynEXMkFI9/wNMOwPOvQFOSTuoEoCO+zyTyUWEkNbUq825ELUQdIsjgmlWUOONudx
|
||||
sAr7ESRXW9QTHVh6uWmr3VRKZHby1JdU3I/wjdlGg5M2dDuXy5nQO9w/nYLjJXiw
|
||||
+zzOetZ/+t7/VOkOpNTeJQhwTM1WF7Y2VLetbi9FHgyzHatrduh07+XEiTbgDf3G
|
||||
Ix2bp2p6oh0G3N2zpiLcK/aZj8rouWWydfFfsU3MZ4FfJDP8I6b9awxjmKYqIr6h
|
||||
iPQCJaLBED8mwK+I5evIbnKv6E6uK+BApWA/R7ElragoFYbqQ1VpvntVMtJt9Dy5
|
||||
ZrI+IQARdXD3bb34oh0IPBhClnvvMUc1cWxDoXEX6oJ4I+LzxE87Zkwnan9qOwen
|
||||
golMVKFwPx1o37qrbmrXID21kKt7FL6xN4HxHLkItr1fKzdyWDFRHgASTAWfx5BI
|
||||
wvPuUW0vZHkvO80VyV2L63whVhPnPASmFkbviomrBttYfpr2aGQqF/qR1Nlxe834
|
||||
MFxk1pS9LMa/WnzvFr0gWakCAwEAAQ==
|
||||
-----END RSA PUBLIC KEY-----
|
||||
`)
|
||||
func setupGenerateKey(homeDir string) (string, error) {
|
||||
secretKeyPairDir := filepath.Join(homeDir, "secrets", "contexts", "contx1", "secrets")
|
||||
if err := os.MkdirAll(secretKeyPairDir, 0777); err != nil {
|
||||
err = fmt.Errorf("Not able to create directories")
|
||||
@@ -207,19 +140,33 @@ MFxk1pS9LMa/WnzvFr0gWakCAwEAAQ==
|
||||
}
|
||||
os.Setenv("QLIKSENSE_KEY_LOCATION", secretKeyPairDir)
|
||||
|
||||
privKeyFile := filepath.Join(secretKeyPairDir, "qliksensePriv")
|
||||
// construct and write priv key file into secretsDir location
|
||||
err := ioutil.WriteFile(privKeyFile, privKeyBytes, 0777)
|
||||
if err != nil {
|
||||
log.Printf("Error while creating file: %v", err)
|
||||
return nil, nil, err
|
||||
key, _ := LoadSecretKey(secretKeyPairDir)
|
||||
|
||||
if key == "" {
|
||||
return GenerateAndStoreSecretKey(secretKeyPairDir)
|
||||
}
|
||||
pubKeyFile := filepath.Join(secretKeyPairDir, "qliksensePub")
|
||||
// construct and write pub key file into secretsDir location
|
||||
err = ioutil.WriteFile(pubKeyFile, publicKeyBytes, 0777)
|
||||
if err != nil {
|
||||
log.Printf("Error while creating file: %v", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
return publicKeyBytes, privKeyBytes, nil
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func Test_set_and_get_fetch_access_token(t *testing.T) {
|
||||
td, homeDir := setup()
|
||||
defer td()
|
||||
createCRFile(homeDir)
|
||||
crFile := filepath.Join("contexts", "contx1", "contx1.yaml")
|
||||
qConfig := NewQConfig(homeDir)
|
||||
newQ, _ := qConfig.SetCrLocation("contx1", crFile)
|
||||
newQ.Write()
|
||||
qConfig = NewQConfig(homeDir)
|
||||
qcr, _ := qConfig.GetCurrentCR()
|
||||
key, _ := qConfig.GetEncryptionKeyFor(qcr.GetName())
|
||||
if err := qcr.SetFetchAccessToken("mytokenbeforeencryption", key); err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
tok := qcr.GetFetchAccessToken(key)
|
||||
if tok != "mytokenbeforeencryption" {
|
||||
t.Log("Expected: mytokenbeforeencryption, got: " + tok)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ func ReadFromFile(content interface{}, sourceFile string) error {
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer file.Close()
|
||||
return ReadFromStream(content, file)
|
||||
}
|
||||
|
||||
|
||||
8
pkg/api/copy.go
Normal file
8
pkg/api/copy.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package api
|
||||
|
||||
import "github.com/otiai10/copy"
|
||||
|
||||
//copy source directory to destination
|
||||
func CopyDirectory(source string, dest string) error {
|
||||
return copy.Copy(source, dest)
|
||||
}
|
||||
101
pkg/api/copy_test.go
Normal file
101
pkg/api/copy_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
"sigs.k8s.io/kustomize/api/krusty"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
func TestCopyDirectory(t *testing.T) {
|
||||
src, _ := ioutil.TempDir("", "")
|
||||
f1, _ := ioutil.TempFile(src, "")
|
||||
ioutil.TempFile(src, "")
|
||||
|
||||
dest, _ := ioutil.TempDir("", "")
|
||||
CopyDirectory(src, dest)
|
||||
if _, err := os.Lstat(filepath.Join(dest, filepath.Base(f1.Name()))); err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyDirectory_withGit_withKuz(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping in short test mode")
|
||||
}
|
||||
|
||||
tmpDir1, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir1)
|
||||
|
||||
tmpDir2, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir2)
|
||||
|
||||
repoPath1 := path.Join(tmpDir1, "repo")
|
||||
repo1, err := kapis_git.CloneRepository(repoPath1, "https://github.com/qlik-oss/qliksense-k8s", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := CopyDirectory(repoPath1, tmpDir2); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
repoPath2 := tmpDir2
|
||||
repo2, err := kapis_git.OpenRepository(repoPath2)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := kapis_git.Checkout(repo2, "v0.0.2", "", nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
repo2Manifest, err := kuz(path.Join(repoPath2, "manifests", "docker-desktop"))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := kapis_git.Checkout(repo1, "v0.0.2", "", nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
repo1Manifest, err := kuz(path.Join(repoPath1, "manifests", "docker-desktop"))
|
||||
if err != nil {
|
||||
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))
|
||||
t.Fatal("expected manifests to be equal, but they were not")
|
||||
}
|
||||
}
|
||||
|
||||
func kuz(directory string) ([]byte, error) {
|
||||
options := &krusty.Options{
|
||||
DoLegacyResourceSort: false,
|
||||
LoadRestrictions: types.LoadRestrictionsNone,
|
||||
DoPrune: false,
|
||||
PluginConfig: konfig.DisabledPluginConfig(),
|
||||
}
|
||||
k := krusty.MakeKustomizer(filesys.MakeFsOnDisk(), options)
|
||||
resMap, err := k.Run(directory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resMap.AsYaml()
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -27,14 +26,14 @@ func (kdcjt *k8sDockerConfigJsonType) GenerateAuth() {
|
||||
}
|
||||
|
||||
type DockerConfigJsonSecret struct {
|
||||
Name string
|
||||
Uri string
|
||||
Username string
|
||||
Password string
|
||||
Email string
|
||||
Name string
|
||||
Uri string
|
||||
Username string
|
||||
Password string
|
||||
Email string
|
||||
}
|
||||
|
||||
func (d *DockerConfigJsonSecret) ToYaml(encryptionKey *rsa.PublicKey) ([]byte, error) {
|
||||
func (d *DockerConfigJsonSecret) ToYaml(encryptionKey string) ([]byte, error) {
|
||||
k8sDockerConfigJson := k8sDockerConfigJsonType{
|
||||
Username: d.Username,
|
||||
Password: d.Password,
|
||||
@@ -51,8 +50,8 @@ func (d *DockerConfigJsonSecret) ToYaml(encryptionKey *rsa.PublicKey) ([]byte, e
|
||||
return nil, err
|
||||
}
|
||||
var k8sDockerConfigJsonMapMaybeEncryptedBytes []byte
|
||||
if encryptionKey != nil {
|
||||
if k8sDockerConfigJsonMapMaybeEncryptedBytes, err = Encrypt(k8sDockerConfigJsonMapBytes, encryptionKey); err != nil {
|
||||
if encryptionKey != "" {
|
||||
if k8sDockerConfigJsonMapMaybeEncryptedBytes, err = EncryptData(k8sDockerConfigJsonMapBytes, encryptionKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
@@ -65,7 +64,7 @@ func (d *DockerConfigJsonSecret) ToYaml(encryptionKey *rsa.PublicKey) ([]byte, e
|
||||
Kind: "Secret",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: d.Name,
|
||||
Name: d.Name,
|
||||
},
|
||||
Type: v1.SecretTypeDockerConfigJson,
|
||||
Data: map[string][]byte{
|
||||
@@ -76,7 +75,7 @@ func (d *DockerConfigJsonSecret) ToYaml(encryptionKey *rsa.PublicKey) ([]byte, e
|
||||
return K8sSecretToYaml(k8sSecret)
|
||||
}
|
||||
|
||||
func (d *DockerConfigJsonSecret) FromYaml(secretBytes []byte, decryptionKey *rsa.PrivateKey) error {
|
||||
func (d *DockerConfigJsonSecret) FromYaml(secretBytes []byte, decryptionKey string) error {
|
||||
k8sDockerConfigJsonMap := k8sDockerConfigJsonMapType{}
|
||||
if k8sSecret, err := K8sSecretFromYaml(secretBytes); err != nil {
|
||||
return err
|
||||
@@ -86,7 +85,7 @@ func (d *DockerConfigJsonSecret) FromYaml(secretBytes []byte, decryptionKey *rsa
|
||||
return errors.New("not a kubernetes.io/dockerconfigjson type")
|
||||
} else if k8sDockerConfigJsonMapEncryptedBytes, ok := k8sSecret.Data[".dockerconfigjson"]; !ok {
|
||||
return errors.New("secret data is missing a value for the .dockerconfigjson key")
|
||||
} else if k8sDockerConfigJsonMapBytes, err := Decrypt(k8sDockerConfigJsonMapEncryptedBytes, decryptionKey); err != nil {
|
||||
} else if k8sDockerConfigJsonMapBytes, err := DecryptData(k8sDockerConfigJsonMapEncryptedBytes, decryptionKey); err != nil {
|
||||
return errors.New("secret data is missing a value for the .dockerconfigjson key")
|
||||
} else if err := json.Unmarshal(k8sDockerConfigJsonMapBytes, &k8sDockerConfigJsonMap); err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -14,21 +12,21 @@ import (
|
||||
|
||||
func TestDockerConfigJsonSecret(t *testing.T) {
|
||||
dockerConfigJsonSecret := DockerConfigJsonSecret{
|
||||
Name: "some-name",
|
||||
Uri: "some-uri",
|
||||
Username: "some-username",
|
||||
Password: "some-password",
|
||||
Email: "some-email",
|
||||
Name: "some-name",
|
||||
Uri: "some-uri",
|
||||
Username: "some-username",
|
||||
Password: "some-password",
|
||||
Email: "some-email",
|
||||
}
|
||||
dockerConfigJsonSecretFromYaml := DockerConfigJsonSecret{}
|
||||
validYamlMap := map[string]interface{}{}
|
||||
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, RSA_KEY_LENGTH)
|
||||
encryptionKey, err := GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("error generating RSA private key: %v\n", err)
|
||||
}
|
||||
|
||||
dockerConfigJsonSecretYamlBytes, err := dockerConfigJsonSecret.ToYaml(&privateKey.PublicKey)
|
||||
dockerConfigJsonSecretYamlBytes, err := dockerConfigJsonSecret.ToYaml(encryptionKey)
|
||||
dockerConfigJsonMap := map[string]interface{}{}
|
||||
if err != nil {
|
||||
t.Fatalf("error converting secret to yaml: %v", err)
|
||||
@@ -43,7 +41,7 @@ func TestDockerConfigJsonSecret(t *testing.T) {
|
||||
t.Fatalf("no .dockerconfigjson data key in the secret yaml: %v", string(dockerConfigJsonSecretYamlBytes))
|
||||
} else if dockerConfigJsonEncryptedBytes, err := base64.StdEncoding.DecodeString(dockerConfigJsonBytesBase64.(string)); err != nil {
|
||||
t.Fatalf("error decoding dockerConfigJsonBytes from base64: %v", err)
|
||||
} else if dockerConfigJsonBytes, err := Decrypt(dockerConfigJsonEncryptedBytes, privateKey); err != nil {
|
||||
} else if dockerConfigJsonBytes, err := DecryptData(dockerConfigJsonEncryptedBytes, encryptionKey); err != nil {
|
||||
t.Fatalf("error decrypting dockerConfigJsonBytes: %v", err)
|
||||
} else if err := json.Unmarshal(dockerConfigJsonBytes, &dockerConfigJsonMap); err != nil {
|
||||
t.Fatalf("error unmarshalling dockerConfigJson from json: %v", err)
|
||||
@@ -63,7 +61,7 @@ func TestDockerConfigJsonSecret(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Logf("dockerConfigJsonSecretYaml: \n%v\n", string(dockerConfigJsonSecretYamlBytes))
|
||||
if err := dockerConfigJsonSecretFromYaml.FromYaml(dockerConfigJsonSecretYamlBytes, privateKey); err != nil {
|
||||
if err := dockerConfigJsonSecretFromYaml.FromYaml(dockerConfigJsonSecretYamlBytes, encryptionKey); err != nil {
|
||||
t.Fatalf("error reading secret in from yaml: %v", err)
|
||||
} else if !reflect.DeepEqual(dockerConfigJsonSecret, dockerConfigJsonSecretFromYaml) {
|
||||
t.Fatalf("secret: %v does not equal secret: %v", dockerConfigJsonSecret, dockerConfigJsonSecretFromYaml)
|
||||
|
||||
@@ -1,58 +1,42 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
RSA_KEY_LENGTH = 4096
|
||||
|
||||
QliksensePublicKey = "qliksensePub"
|
||||
QliksensePrivateKey = "qliksensePriv"
|
||||
key_file_name = "user_secret_key"
|
||||
)
|
||||
|
||||
// GenerateAndStoreSecretKeypair generates and stores key pairs
|
||||
func GenerateAndStoreSecretKeypair(secretsPath string) error {
|
||||
LogDebugMessage("%s exists", secretsPath)
|
||||
// creating contexts/qlik-default/secrets/qliksensePub and contexts/qlik-default/secrets/qliksensePriv files
|
||||
publicKeyFilePath := filepath.Join(secretsPath, QliksensePublicKey)
|
||||
privateKeyFilePath := filepath.Join(secretsPath, QliksensePrivateKey)
|
||||
LogDebugMessage("Generating public-private key pair.....")
|
||||
GenerateRSAEncryptionKeys(publicKeyFilePath, privateKeyFilePath)
|
||||
LogDebugMessage("Generated public-private key pairs")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateRSAEncryptionKeys is used to generate a new public-private key pair
|
||||
func GenerateRSAEncryptionKeys(publicKeyFilePath, privateKeyFilePath string) error {
|
||||
LogDebugMessage("Generating new RSA key pair")
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, RSA_KEY_LENGTH)
|
||||
// GenerateAndStoreSecretKey generates and stores key
|
||||
func GenerateAndStoreSecretKey(secretsDir string) (string, error) {
|
||||
// creating contexts/qlik-default/secrets/user_secret_key
|
||||
keyFile := filepath.Join(secretsDir, key_file_name)
|
||||
key, err := GenerateKey()
|
||||
if err != nil {
|
||||
log.Printf("error generating RSA private key: %v\n", err)
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
privateKeyPEM := EncodePrivateKey(privateKey)
|
||||
if err := writeContentToFile(privateKeyPEM, privateKeyFilePath); err != nil {
|
||||
return err
|
||||
if err := writeContentToFile([]byte(key), keyFile); err != nil {
|
||||
return "", err
|
||||
}
|
||||
pubKeyPEM, err2 := EncodePublicKey(&privateKey.PublicKey)
|
||||
if err2 != nil {
|
||||
log.Printf("error occurred when encoding public key: %v\n", err2)
|
||||
return err2
|
||||
return key, nil
|
||||
}
|
||||
func LoadSecretKey(secretsDir string) (string, error) {
|
||||
keyFile := filepath.Join(secretsDir, key_file_name)
|
||||
by, err := ioutil.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := writeContentToFile(pubKeyPEM, publicKeyFilePath); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return string(by), nil
|
||||
}
|
||||
|
||||
// writeContentToFile writes keys to a file
|
||||
@@ -65,104 +49,54 @@ func writeContentToFile(keyData []byte, fileName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts data with public key
|
||||
func Encrypt(pt []byte, pub *rsa.PublicKey) ([]byte, error) {
|
||||
//hash := sha512.New()
|
||||
//ct, err := rsa.EncryptOAEP(hash, rand.Reader, pub, pt, nil)
|
||||
ct, err := rsa.EncryptPKCS1v15(rand.Reader, pub, pt)
|
||||
func GenerateKey() (string, error) {
|
||||
salt := make([]byte, 32)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return "", err
|
||||
}
|
||||
s := fmt.Sprintf("%x", salt)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func EncryptData(plaintext []byte, userKey string) ([]byte, error) {
|
||||
key, _ := hex.DecodeString(userKey)
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
return ct, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts data with private key
|
||||
func Decrypt(ct []byte, priv *rsa.PrivateKey) ([]byte, error) {
|
||||
// hash := sha512.New()
|
||||
// plaintext, err := rsa.DecryptOAEP(hash, rand.Reader, priv, ciphertext, nil)
|
||||
pt, err := rsa.DecryptPKCS1v15(rand.Reader, priv, ct)
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
return pt, nil
|
||||
nonce := make([]byte, aesgcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return aesgcm.Seal(nonce, nonce, plaintext, nil), nil
|
||||
}
|
||||
|
||||
// EncodePrivateKey private key to bytes
|
||||
func EncodePrivateKey(priv *rsa.PrivateKey) []byte {
|
||||
privBytes := pem.EncodeToMemory(
|
||||
&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(priv),
|
||||
},
|
||||
)
|
||||
|
||||
return privBytes
|
||||
}
|
||||
|
||||
// EncodePublicKey public key to bytes
|
||||
func EncodePublicKey(pub *rsa.PublicKey) ([]byte, error) {
|
||||
pubASN1, err := x509.MarshalPKIXPublicKey(pub)
|
||||
func DecryptData(ciphertext []byte, userKey string) ([]byte, error) {
|
||||
key, _ := hex.DecodeString(userKey)
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubBytes := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PUBLIC KEY",
|
||||
Bytes: pubASN1,
|
||||
})
|
||||
|
||||
return pubBytes, nil
|
||||
}
|
||||
|
||||
// DecodeToPrivateKey bytes to private key
|
||||
func DecodeToPrivateKey(priv []byte) (*rsa.PrivateKey, error) {
|
||||
block, _ := pem.Decode(priv)
|
||||
enc := x509.IsEncryptedPEMBlock(block)
|
||||
b := block.Bytes
|
||||
var err error
|
||||
if enc {
|
||||
log.Println("is encrypted pem block")
|
||||
b, err = x509.DecryptPEMBlock(block, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
key, err := x509.ParsePKCS1PrivateKey(b)
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// DecodeToPublicKey bytes to public key
|
||||
func DecodeToPublicKey(pub []byte) (*rsa.PublicKey, error) {
|
||||
block, _ := pem.Decode(pub)
|
||||
enc := x509.IsEncryptedPEMBlock(block)
|
||||
b := block.Bytes
|
||||
var err error
|
||||
if enc {
|
||||
log.Println("is encrypted pem block")
|
||||
b, err = x509.DecryptPEMBlock(block, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
nonceSize := aesgcm.NonceSize()
|
||||
if len(ciphertext) < nonceSize {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
iface, err := x509.ParsePKIXPublicKey(b)
|
||||
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
key, ok := iface.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
err := fmt.Errorf("Unable to decode public key")
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
@@ -1,128 +1,29 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_generateRSAEncryptionKeys(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid case",
|
||||
wantErr: false,
|
||||
},
|
||||
func Test_encrypt_decrypt(t *testing.T) {
|
||||
key, err := GenerateKey()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := GenerateAndStoreSecretKeypair(os.TempDir()); (err != nil) != tt.wantErr {
|
||||
t.Errorf("generateRSAEncryptionKeys() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_encryption_decryption(t *testing.T) {
|
||||
privKeyBytes := []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJJwIBAAKCAgEAwUCimKCidbF3UxEHPy8K+hvhklRB9JYhj5sJy0if4lTVibkK
|
||||
1MrYCykOnmC40pPU9GLY1b8HxAg9tvyRn0YHUxOra6vVQaVcOVJhTM8D18d+lSr3
|
||||
Lp1yiX+UGT4nzWI9+R1CCbwXrqeQVoZs6QZKynEXMkFI9/wNMOwPOvQFOSTuoEoC
|
||||
O+zyTyUWEkNbUq825ELUQdIsjgmlWUOONudxsAr7ESRXW9QTHVh6uWmr3VRKZHby
|
||||
1JdU3I/wjdlGg5M2dDuXy5nQO9w/nYLjJXiw+zzOetZ/+t7/VOkOpNTeJQhwTM1W
|
||||
F7Y2VLetbi9FHgyzHatrduh07+XEiTbgDf3GIx2bp2p6oh0G3N2zpiLcK/aZj8ro
|
||||
uWWydfFfsU3MZ4FfJDP8I6b9awxjmKYqIr6hiPQCJaLBED8mwK+I5evIbnKv6E6u
|
||||
K+BApWA/R7ElragoFYbqQ1VpvntVMtJt9Dy5ZrI+IQARdXD3bb34oh0IPBhClnvv
|
||||
MUc1cWxDoXEX6oJ4I+LzxE87Zkwnan9qOwengolMVKFwPx1o37qrbmrXID21kKt7
|
||||
FL6xN4HxHLkItr1fKzdyWDFRHgASTAWfx5BIwvPuUW0vZHkvO80VyV2L63whVhPn
|
||||
PASmFkbviomrBttYfpr2aGQqF/qR1Nlxe834MFxk1pS9LMa/WnzvFr0gWakCAwEA
|
||||
AQKCAgARSp9B2N2wejibDiL/3E23I1eDqFZedDB8kPrHXbAwqDaTJCN79spt9TaB
|
||||
pVXkQaYEV/Pe7EDdoX8kKGU/QxzUqiXkdHOYdBtUZbKfFMbbP9ZrsnR7j0r4UpoF
|
||||
yDH3hprU93E5PcNAtW2M0GpeT1nR01yn+n908PCdOAIE3GC7RDq1zOl2QzVLL55R
|
||||
9ATv2Q2oTvJ/ETc7XlGVMx4+e2cIwXLFjeLjLI6pSYlxnarrGuetJZeEviWxto9n
|
||||
odFVZI6yx8JFTXX8ZTCr/1IjwDDVyhMPmrHI2Lsv9cqBpSpbVe32cUkKxhsGaYjz
|
||||
GvesQKamOPhco2ATNxPm0yopFlPsGKMfVl0BK0J6BqFh1BvU/SYJmXfnFuUNO3vV
|
||||
4u2Saa0q1iddxV0rXDwIqUfn+S6rwzK0G7y8bH2yvpB2VwiG3TFPnULep4wsefNq
|
||||
Fj92kqFBjacGpQLEEslUY0CMgeZ2+NuBQSUTscP3wBRsottMR6YXJtINdvfHBx+e
|
||||
EcN71z8D00w3mYqIQ7qb4Ml6HOqknunn58g43L9sACMUMTlEBXa9pUnScNYgWBAz
|
||||
W2q2mH37cIydM2JRZPpA8B4yTHt5ugJmChwyNFM7941arjKrebH+6AzLkofGedOP
|
||||
zg+vZQuPEXWs+3MBBnkWoyJW3Y0fbQdjsuQTtnd+7iyoxoBroQKCAQEA4dIiFlIS
|
||||
MDfRhQQWSiDvaw9aneDEJ3uo63ZRH5tm/IynLgtjYgEm/ZxlBCQgqRKLYELBxhu8
|
||||
SaF0uPK8pmpFJt0mIwSlsdeVhuE2obQeKUCczaqrKeaHS3PdWLjTlwph81BGRkHy
|
||||
qfqtNylyyMxrdEbnR51EtsWgFq6anTUAui1Q09JMuMNZRMOzDs1F4gExgD22rc0V
|
||||
c9YQ+jHJRxBGtNKMpMEqc8cvaxBidbItrN9SMTSWog7uYPBuEuaJ6K9vpgyJMOzJ
|
||||
SYcQEFGqgIqIDCg+ABE4d/4YROMKZ1DV/bJCind9brUHSx6XALsF0nC5c1Q9TnUL
|
||||
qI2khOwts4KYKwKCAQEA2xRC6Az97Vkdzu7BjLJ1FKmx4S2nEEgVS12ds82U+5Xf
|
||||
BHKAJnjqlqmmpzzJG+d77IYktz0+mey1QCNkqlm2fhuKs8LZMnpZRf0l8VcoBsUP
|
||||
/xKz7wfiE7RRFZtLJhPp4hhe43GzX5/JFMWMnC6UykwQbj4t1E/GNM/Suqwvg12M
|
||||
wktAJ6nqLgfhjQSO4xWo+nPzcbX+fNtrPCZVrBhYXihhcwRRNImWUCGJ6J4LMdPY
|
||||
Y9Z59qhOvE9cReH/Xw1av46omyiSyAqlgPyZ/kzA2IJSqYCjiQR/2+RD/g13jpcJ
|
||||
jatXLVZ8MJSL5OTS40G/HHTNNpNHbKKh0GOyxBA3ewKCAQBAn8UXhCcmW2L/YPsL
|
||||
/b7mcX9qPP+FmRLvR23R0MQ5M/tH5wRq8I969n3GIJykJeVzB8eybQ+GNslTgEvS
|
||||
iAkAJTubu+G7MkndTqg2wHf9MDtvdA8Fr646Po8yq7oJuHPtkKR7yLWsRUu6xIbP
|
||||
xgheP0hCq1QVxhqZQyCGKrvpi7xc0gsYuPbcAfFFJCOCmPrUi1SzCkTAYJt9LjA+
|
||||
wP6rErIjGBCRD4iXaBn1OqdtmH9KC5WsDP/VCBlIGWeQCly2NVIxiSHVg+xp7yUP
|
||||
IhXq/L05gbQaSsIhPKQmivCiaJg4The8TdwneDqYf+0bmxzHT203/bD3bImPbJNr
|
||||
ksz/AoIBAEwu4Y1cZzkQUmNRd5D7xecnk6ngfEYXKwCIT3zlMrfCSEl9n77BMaKu
|
||||
4Dsr0iuX9eosQ7xM2eYhAG6LYEg05lc4MKWOToVVMpI6E+W3Dz47bPKgiF3I+f8s
|
||||
Jz5CQIG/TwfGvciOE3hfUkec4ua09BzdEqGjkcBQ9XYMBxXPJr6h2379OBQS7FKR
|
||||
fwfQ2/dv4tElXTTfut2kV8gU9Jnh5Wjo1epvR+XjKpg28YQo4W+0YX1magcyRB8L
|
||||
4eSTUIC3XiVa8Jr0IwbZXPBb5xkdi7o+p4w2JahSHjxTRqmj+T1mnHXdbXVgq9Mg
|
||||
9Pzl7cgFZvX4UBx4XtASRf73jITNtt0CggEADH9K+O7FrIOSQly0sMvsRCMtejp3
|
||||
o+MDh1Q+vEg2kEgNXjS4ZFVljUpM2kg1OdUz7feS4dLXUJiIQ8ZWtZPedcq7wjHd
|
||||
02he5+s06l0jPifN3tX1ADfXGpXg5R2fbkrIzakkPP5/RO/aDxIUo7qhklNsVTXO
|
||||
VlGGfWLdk0ekA4upKm02Q1+YOlbIcAicEYYY8K7IffUwnohzKwL9yfuGi1VKTXpE
|
||||
4fzdegsHI03FSqR7V+LvtBpIupQ7RO4kuBmCEyI4E9FVknchg4te4gO3qwd9y0rJ
|
||||
Gu7HNIOrwOHzviI7J6Nd/l9MmeKqklHSgJvko/f5TmiXuQQ8xDZf84rcjQ==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`)
|
||||
|
||||
publicKeyBytes := []byte(`-----BEGIN RSA PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwUCimKCidbF3UxEHPy8K
|
||||
+hvhklRB9JYhj5sJy0if4lTVibkK1MrYCykOnmC40pPU9GLY1b8HxAg9tvyRn0YH
|
||||
UxOra6vVQaVcOVJhTM8D18d+lSr3Lp1yiX+UGT4nzWI9+R1CCbwXrqeQVoZs6QZK
|
||||
ynEXMkFI9/wNMOwPOvQFOSTuoEoCO+zyTyUWEkNbUq825ELUQdIsjgmlWUOONudx
|
||||
sAr7ESRXW9QTHVh6uWmr3VRKZHby1JdU3I/wjdlGg5M2dDuXy5nQO9w/nYLjJXiw
|
||||
+zzOetZ/+t7/VOkOpNTeJQhwTM1WF7Y2VLetbi9FHgyzHatrduh07+XEiTbgDf3G
|
||||
Ix2bp2p6oh0G3N2zpiLcK/aZj8rouWWydfFfsU3MZ4FfJDP8I6b9awxjmKYqIr6h
|
||||
iPQCJaLBED8mwK+I5evIbnKv6E6uK+BApWA/R7ElragoFYbqQ1VpvntVMtJt9Dy5
|
||||
ZrI+IQARdXD3bb34oh0IPBhClnvvMUc1cWxDoXEX6oJ4I+LzxE87Zkwnan9qOwen
|
||||
golMVKFwPx1o37qrbmrXID21kKt7FL6xN4HxHLkItr1fKzdyWDFRHgASTAWfx5BI
|
||||
wvPuUW0vZHkvO80VyV2L63whVhPnPASmFkbviomrBttYfpr2aGQqF/qR1Nlxe834
|
||||
MFxk1pS9LMa/WnzvFr0gWakCAwEAAQ==
|
||||
-----END RSA PUBLIC KEY-----
|
||||
`)
|
||||
origStr := "Value1234"
|
||||
|
||||
pubKey, err := DecodeToPublicKey(publicKeyBytes)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
privKey, err := DecodeToPrivateKey(privKeyBytes)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
encData, err := Encrypt([]byte(origStr), pubKey)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
encDataStr := base64.StdEncoding.EncodeToString(encData)
|
||||
log.Println("Encoded text:", encDataStr)
|
||||
dec, _ := base64.StdEncoding.DecodeString(encDataStr)
|
||||
data, err := Decrypt(dec, privKey)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if string(data) != origStr {
|
||||
t.Error("original string and decrypted string don't match")
|
||||
t.FailNow()
|
||||
testData := "this is a secret value"
|
||||
enc, err := EncryptData([]byte(testData), key)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
dec, err := DecryptData(enc, key)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if testData != string(dec) {
|
||||
t.Log("expected: " + testData)
|
||||
t.Log("actual: " + string(dec))
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,20 @@ func KubectlApply(manifests, namespace string) error {
|
||||
return kubectlOperation(manifests, "apply", namespace)
|
||||
}
|
||||
|
||||
func KubectlApplyVerbose(manifests, namespace string, verbose bool) error {
|
||||
return kubectlOperationVerbose(manifests, "apply", namespace, verbose)
|
||||
}
|
||||
|
||||
// KubectlDelete delete resoruces in the provided namespace,
|
||||
// if namespace="" then use whatever the kubectl default is
|
||||
func KubectlDelete(manifests, namespace string) error {
|
||||
return kubectlOperation(manifests, "delete", namespace)
|
||||
}
|
||||
|
||||
func KubectlDeleteVerbose(manifests, namespace string, verbose bool) error {
|
||||
return kubectlOperationVerbose(manifests, "delete", namespace, verbose)
|
||||
}
|
||||
|
||||
func GetKubectlNamespace() string {
|
||||
namespace := ""
|
||||
cmd := exec.Command("kubectl", "config", "current-context")
|
||||
@@ -61,6 +69,10 @@ func SetKubectlNamespace(ns string) {
|
||||
}
|
||||
|
||||
func kubectlOperation(manifests string, oprName string, namespace string) error {
|
||||
return kubectlOperationVerbose(manifests, oprName, namespace, true)
|
||||
}
|
||||
|
||||
func kubectlOperationVerbose(manifests string, oprName string, namespace string, verbose bool) error {
|
||||
tempYaml, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
fmt.Println("cannot create file ", err)
|
||||
@@ -88,12 +100,16 @@ func kubectlOperation(manifests string, oprName string, namespace string) error
|
||||
}
|
||||
|
||||
sterrBuffer := &bytes.Buffer{}
|
||||
cmd.Stdout = os.Stdout
|
||||
stoutBuffer := &bytes.Buffer{}
|
||||
cmd.Stdout = stoutBuffer
|
||||
cmd.Stderr = sterrBuffer
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("kubectl %v failed with: %v, %v, temp k8s yaml file:%v\n", oprName, err, sterrBuffer.String(), tempYaml.Name())
|
||||
}
|
||||
if verbose {
|
||||
fmt.Println(stoutBuffer.String())
|
||||
}
|
||||
os.Remove(tempYaml.Name())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package preflight
|
||||
package api
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
api "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
@@ -43,8 +44,10 @@ func NewPreflightConfig(qHome string) *PreflightConfig {
|
||||
if _, err := os.Lstat(conFile); err != nil {
|
||||
return p
|
||||
}
|
||||
p = &PreflightConfig{}
|
||||
if err := api.ReadFromFile(p, conFile); err != nil {
|
||||
p = &PreflightConfig{
|
||||
QliksenseHomePath: qHome,
|
||||
}
|
||||
if err := ReadFromFile(p, conFile); err != nil {
|
||||
return nil
|
||||
}
|
||||
return p
|
||||
@@ -61,7 +64,7 @@ func (p *PreflightConfig) Write() error {
|
||||
if err := os.MkdirAll(pDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
return api.WriteToFile(p, p.GetConfigFilePath())
|
||||
return WriteToFile(p, p.GetConfigFilePath())
|
||||
}
|
||||
|
||||
func (p *PreflightConfig) AddMinK8sV(version string) {
|
||||
@@ -78,11 +81,22 @@ func (p *PreflightConfig) AddImage(imageFor, imageName string) {
|
||||
p.Spec.Images[imageFor] = imageName
|
||||
}
|
||||
|
||||
func (p *PreflightConfig) GetImageName(imageFor string) string {
|
||||
func (p *PreflightConfig) GetImageName(imageFor string, accountForImageRegistry bool) (string, error) {
|
||||
if p.Spec.Images == nil {
|
||||
return ""
|
||||
return "", nil
|
||||
}
|
||||
return p.Spec.Images[imageFor]
|
||||
image := p.Spec.Images[imageFor]
|
||||
if accountForImageRegistry {
|
||||
qConfig := NewQConfig(p.QliksenseHomePath)
|
||||
if currentCR, err := qConfig.GetCurrentCR(); err != nil {
|
||||
return "", err
|
||||
} else if imageRegistry := currentCR.Spec.GetImageRegistry(); imageRegistry != "" {
|
||||
imageSegments := strings.Split(image, "/")
|
||||
imageNameAndTag := imageSegments[len(imageSegments)-1]
|
||||
return path.Join(imageRegistry, imageNameAndTag), nil
|
||||
}
|
||||
}
|
||||
return image, nil
|
||||
}
|
||||
func (p *PreflightConfig) GetMinK8sVersion() string {
|
||||
return p.Spec.MinK8sVersion
|
||||
@@ -105,5 +119,6 @@ func (p *PreflightConfig) Initialize() error {
|
||||
p.AddMinK8sV("1.15")
|
||||
p.AddImage("nginx", "nginx")
|
||||
p.AddImage("netcat", "subfuzion/netcat")
|
||||
p.AddImage("mongo", "mongo")
|
||||
return p.Write()
|
||||
}
|
||||
138
pkg/api/preflight_apis_test.go
Normal file
138
pkg/api/preflight_apis_test.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Initalize(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
validate func(t *testing.T, tempDir string)
|
||||
}{
|
||||
{
|
||||
name: "without account for imageRegistry",
|
||||
validate: func(t *testing.T, tempDir string) {
|
||||
preflightConfig := NewPreflightConfig(tempDir)
|
||||
imageName, err := preflightConfig.GetImageName("test", false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if imageName != "testimage" {
|
||||
t.Fatalf("expected image name: testimage, got: %v", imageName)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with account for configured imageRegistry",
|
||||
validate: func(t *testing.T, tempDir string) {
|
||||
registry := "registryFoo"
|
||||
setupQliksenseTestDefaultContext(t, tempDir, fmt.Sprintf(`
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
metadata:
|
||||
name: qlik-default
|
||||
spec:
|
||||
configs:
|
||||
qliksense:
|
||||
- name: imageRegistry
|
||||
value: %v
|
||||
`, registry))
|
||||
preflightConfig := NewPreflightConfig(tempDir)
|
||||
imageName, err := preflightConfig.GetImageName("test", true)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
expectedImageName := fmt.Sprintf("%v/testimage", registry)
|
||||
if imageName != expectedImageName {
|
||||
t.Fatalf("expected image name: %v, got: %v", expectedImageName, imageName)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with account for un-configured imageRegistry",
|
||||
validate: func(t *testing.T, tempDir string) {
|
||||
setupQliksenseTestDefaultContext(t, tempDir, `
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
metadata:
|
||||
name: qlik-default
|
||||
spec:
|
||||
configs:
|
||||
qliksense:
|
||||
- name: something
|
||||
value: other
|
||||
`)
|
||||
preflightConfig := NewPreflightConfig(tempDir)
|
||||
imageName, err := preflightConfig.GetImageName("test", true)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
expectedImageName := "testimage"
|
||||
if imageName != expectedImageName {
|
||||
t.Fatalf("expected image name: %v, got: %v", expectedImageName, imageName)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
setupPreflightConfig(t, tempDir)
|
||||
testCase.validate(t, tempDir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setupPreflightConfig(t *testing.T, tempDir string) {
|
||||
pf := NewPreflightConfig(tempDir)
|
||||
if err := pf.Initialize(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p := &PreflightConfig{
|
||||
QliksenseHomePath: tempDir,
|
||||
}
|
||||
if err := ReadFromFile(p, pf.GetConfigFilePath()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p.GetMinK8sVersion() != "1.15" {
|
||||
t.Fatalf("expected k8 version: 1.15, but got " + p.GetMinK8sVersion())
|
||||
}
|
||||
p.AddImage("test", "testimage")
|
||||
if err := p.Write(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func setupQliksenseTestDefaultContext(t *testing.T, tmpQlikSenseHome, CR string) {
|
||||
if err := ioutil.WriteFile(path.Join(tmpQlikSenseHome, "config.yaml"), []byte(`
|
||||
apiVersion: config.qlik.com/v1
|
||||
kind: QliksenseConfig
|
||||
metadata:
|
||||
name: QliksenseConfigMetadata
|
||||
spec:
|
||||
contexts:
|
||||
- name: qlik-default
|
||||
crFile: contexts/qlik-default/qlik-default.yaml
|
||||
currentContext: qlik-default
|
||||
`), os.ModePerm); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
defaultContextDir := path.Join(tmpQlikSenseHome, "contexts", "qlik-default")
|
||||
if err := os.MkdirAll(defaultContextDir, os.ModePerm); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path.Join(defaultContextDir, "qlik-default.yaml"), []byte(CR), os.ModePerm); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
b64 "encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -46,7 +46,7 @@ func DirExists(dirname string) bool {
|
||||
// LogDebugMessage logs a debug message
|
||||
func LogDebugMessage(strMessage string, args ...interface{}) {
|
||||
if os.Getenv("QLIKSENSE_DEBUG") == "true" {
|
||||
log.Printf(strMessage, args...)
|
||||
fmt.Printf(strMessage, args...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,27 +62,38 @@ func ReadKeys(keyFile string) ([]byte, error) {
|
||||
}
|
||||
|
||||
// ProcessConfigArgs processes args and returns an service, key, value slice
|
||||
func ProcessConfigArgs(args []string) ([]*ServiceKeyValue, error) {
|
||||
func ProcessConfigArgs(args []string, base64Encoded bool) ([]*ServiceKeyValue, error) {
|
||||
// prepare received args
|
||||
// split args[0] into key and value
|
||||
if len(args) == 0 {
|
||||
err := fmt.Errorf("No args were provided. Please provide args to configure the current context")
|
||||
return nil, err
|
||||
}
|
||||
notValidErr := fmt.Errorf("Please provide valid args for this command")
|
||||
resultSvcKV := make([]*ServiceKeyValue, len(args))
|
||||
re1 := regexp.MustCompile(`([\w\-]{1,}).([\w\-]{1,})=("*[\w\-?=_/:0-9\.]+"*)`)
|
||||
// qliksense.mongodb=somethig
|
||||
for i, arg := range args {
|
||||
LogDebugMessage("Arg received: %s", arg)
|
||||
result := re1.FindStringSubmatch(arg)
|
||||
// check if result array's length is == 4 (index 0 - is the full match & indices 1,2,3- are the fields we need)
|
||||
if len(result) != 4 {
|
||||
err := fmt.Errorf("Please provide valid args for this command")
|
||||
return nil, err
|
||||
first := strings.SplitN(arg, "=", 2)
|
||||
if len(first) != 2 {
|
||||
return nil, notValidErr
|
||||
}
|
||||
second := strings.SplitN(first[0], ".", 2)
|
||||
if len(second) != 2 {
|
||||
return nil, notValidErr
|
||||
}
|
||||
resultValue := strings.Trim(first[1], "\"")
|
||||
if base64Encoded {
|
||||
if decodeByte, err := b64.StdEncoding.DecodeString(resultValue); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
resultValue = strings.Trim(string(decodeByte), "\n ")
|
||||
}
|
||||
}
|
||||
resultSvcKV[i] = &ServiceKeyValue{
|
||||
SvcName: result[1],
|
||||
Key: result[2],
|
||||
Value: strings.ReplaceAll(result[3], `"`, ""),
|
||||
SvcName: second[0],
|
||||
Key: second[1],
|
||||
Value: resultValue,
|
||||
}
|
||||
}
|
||||
return resultSvcKV, nil
|
||||
|
||||
@@ -11,11 +11,12 @@ func TestProcessConfigArgs(t *testing.T) {
|
||||
"test-dash.dash-key=value-dash",
|
||||
"test-dot.dot-key=127.0.0.1",
|
||||
"test123.key123=value123",
|
||||
"test-equal.keyequal=newvalue=@hj",
|
||||
}
|
||||
expectedKeys := []string{"mongodb", "test", "dash-key", "dot-key", "key123"}
|
||||
expectedValue := []string{"mongouri://something?ffall", "value_under", "value-dash", "127.0.0.1", "value123"}
|
||||
exppectedSvc := []string{"qliksense", "test_under", "test-dash", "test-dot", "test123"}
|
||||
sv, err := ProcessConfigArgs(args)
|
||||
expectedKeys := []string{"mongodb", "test", "dash-key", "dot-key", "key123", "keyequal"}
|
||||
expectedValue := []string{"mongouri://something?ffall", "value_under", "value-dash", "127.0.0.1", "value123", "newvalue=@hj"}
|
||||
exppectedSvc := []string{"qliksense", "test_under", "test-dash", "test-dot", "test123", "test-equal"}
|
||||
sv, err := ProcessConfigArgs(args, false)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
|
||||
@@ -2,96 +2,110 @@ package preflight
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
ansi "github.com/mattn/go-colorable"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/ttacon/chalk"
|
||||
)
|
||||
|
||||
func (qp *QliksensePreflight) RunAllPreflightChecks(namespace string, kubeConfigContents []byte, mongodbUrl string) {
|
||||
|
||||
func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions) error {
|
||||
checkCount := 0
|
||||
totalCount := 0
|
||||
|
||||
out := ansi.NewColorableStdout()
|
||||
// Preflight minimum kuberenetes version check
|
||||
fmt.Printf("\nPreflight kubernetes minimum version check\n")
|
||||
fmt.Println("------------------------------------------")
|
||||
if err := qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Printf("Preflight kubernetes minimum version check: FAILED\n")
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight kubernetes minimum version check FAILED"))
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n\n", chalk.Green.Color("Preflight kubernetes minimum version check PASSED"))
|
||||
checkCount++
|
||||
}
|
||||
totalCount++
|
||||
|
||||
// Preflight deployment check
|
||||
fmt.Printf("\nPreflight deployment check\n")
|
||||
fmt.Println("--------------------------")
|
||||
if err := qp.CheckDeployment(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Printf("Preflight deployment check: FAILED\n")
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight deployment check FAILED"))
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n\n", chalk.Green.Color("Preflight deployment check PASSED"))
|
||||
checkCount++
|
||||
}
|
||||
totalCount++
|
||||
|
||||
// Preflight service check
|
||||
fmt.Printf("\nPreflight service check\n")
|
||||
fmt.Println("-----------------------")
|
||||
if err := qp.CheckService(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Printf("Preflight service check: FAILED\n")
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight service check FAILED"))
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n\n", chalk.Green.Color("Preflight service check PASSED"))
|
||||
checkCount++
|
||||
}
|
||||
totalCount++
|
||||
|
||||
// Preflight pod check
|
||||
fmt.Printf("\nPreflight pod check\n")
|
||||
fmt.Println("-----------------------")
|
||||
if err := qp.CheckPod(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Printf("Preflight pod check: FAILED\n")
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight pod check FAILED"))
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n\n", chalk.Green.Color("Preflight pod check PASSED"))
|
||||
checkCount++
|
||||
}
|
||||
totalCount++
|
||||
|
||||
// Preflight role check
|
||||
fmt.Printf("\nPreflight role check\n")
|
||||
fmt.Println("--------------------------")
|
||||
if err := qp.CheckCreateRole(namespace); err != nil {
|
||||
fmt.Printf("Preflight role check: FAILED\n")
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color("Preflight role check FAILED"))
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n\n", chalk.Green.Color("Preflight role check PASSED"))
|
||||
checkCount++
|
||||
}
|
||||
totalCount++
|
||||
|
||||
// Preflight rolebinding check
|
||||
fmt.Printf("\nPreflight rolebinding check\n")
|
||||
fmt.Println("---------------------------------")
|
||||
if err := qp.CheckCreateRoleBinding(namespace); err != nil {
|
||||
fmt.Printf("Preflight rolebinding check: FAILED\n")
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color(" Preflight rolebinding check FAILED"))
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n\n", chalk.Green.Color("Preflight rolebinding check PASSED"))
|
||||
checkCount++
|
||||
}
|
||||
totalCount++
|
||||
|
||||
// Preflight serviceaccount check
|
||||
fmt.Printf("\nPreflight serviceaccount check\n")
|
||||
fmt.Println("------------------------------------")
|
||||
if err := qp.CheckCreateServiceAccount(namespace); err != nil {
|
||||
fmt.Printf("Preflight serviceaccount check: FAILED\n")
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color(" Preflight serviceaccount check FAILED"))
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n\n", chalk.Green.Color("Preflight serviceaccount check PASSED"))
|
||||
checkCount++
|
||||
}
|
||||
totalCount++
|
||||
|
||||
// Preflight mongo check
|
||||
fmt.Printf("\nPreflight mongo check\n")
|
||||
fmt.Println("---------------------")
|
||||
if err := qp.CheckMongo(kubeConfigContents, namespace, mongodbUrl); err != nil {
|
||||
fmt.Printf("Preflight mongo check: FAILED\n")
|
||||
if err := qp.CheckMongo(kubeConfigContents, namespace, preflightOpts); err != nil {
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color(" Preflight mongo check FAILED"))
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n\n", chalk.Green.Color("Preflight mongo check PASSED"))
|
||||
checkCount++
|
||||
}
|
||||
totalCount++
|
||||
|
||||
// Preflight DNS check
|
||||
fmt.Printf("\nPreflight DNS check\n")
|
||||
fmt.Println("-------------------")
|
||||
if err := qp.CheckDns(namespace, kubeConfigContents); err != nil {
|
||||
fmt.Printf("Preflight DNS check: FAILED\n")
|
||||
fmt.Fprintf(out, "%s\n", chalk.Red.Color(" Preflight DNS check FAILED"))
|
||||
fmt.Printf("Error: %v\n\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n\n", chalk.Green.Color("Preflight DNS check PASSED"))
|
||||
checkCount++
|
||||
}
|
||||
totalCount++
|
||||
|
||||
if checkCount == 9 {
|
||||
fmt.Printf("\nAll preflight checks have PASSED\n")
|
||||
} else {
|
||||
fmt.Printf("\n1 or more preflight checks have FAILED\n")
|
||||
if checkCount == totalCount {
|
||||
// All preflight checks were successful
|
||||
return nil
|
||||
}
|
||||
fmt.Println("Completed running all preflight checks")
|
||||
return errors.New("1 or more preflight checks have FAILED")
|
||||
}
|
||||
|
||||
@@ -10,18 +10,18 @@ func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigConten
|
||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Kube config error: %v\n", err)
|
||||
fmt.Print(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Deployment check
|
||||
fmt.Printf("Preflight deployment check: \n")
|
||||
qp.P.LogVerboseMessage("Preflight deployment check: \n")
|
||||
qp.P.LogVerboseMessage("--------------------------- \n")
|
||||
err = qp.checkPfDeployment(clientset, namespace, "deployment-preflight-check")
|
||||
if err != nil {
|
||||
fmt.Println("Preflight Deployment check: FAILED")
|
||||
qp.P.LogVerboseMessage("Preflight Deployment check: FAILED\n")
|
||||
return err
|
||||
}
|
||||
fmt.Println("Completed preflight deployment check")
|
||||
qp.P.LogVerboseMessage("Completed preflight deployment check\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -29,18 +29,18 @@ func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigConten
|
||||
func (qp *QliksensePreflight) CheckService(namespace string, kubeConfigContents []byte) error {
|
||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
|
||||
return err
|
||||
}
|
||||
// Service check
|
||||
fmt.Printf("\nPreflight service check: \n")
|
||||
err = checkPfService(clientset, namespace)
|
||||
qp.P.LogVerboseMessage("Preflight service check: \n")
|
||||
qp.P.LogVerboseMessage("------------------------ \n")
|
||||
err = qp.checkPfService(clientset, namespace)
|
||||
if err != nil {
|
||||
fmt.Println("Preflight Service check: FAILED")
|
||||
qp.P.LogVerboseMessage("Preflight Service check: FAILED\n")
|
||||
return err
|
||||
}
|
||||
fmt.Println("Completed preflight service check")
|
||||
qp.P.LogVerboseMessage("Completed preflight service check\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -48,71 +48,80 @@ func (qp *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []by
|
||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
|
||||
fmt.Print(err)
|
||||
return err
|
||||
}
|
||||
// Pod check
|
||||
fmt.Printf("\nPreflight pod check: \n")
|
||||
|
||||
qp.P.LogVerboseMessage("Preflight pod check: \n")
|
||||
qp.P.LogVerboseMessage("-------------------- \n")
|
||||
err = qp.checkPfPod(clientset, namespace)
|
||||
if err != nil {
|
||||
fmt.Println("Preflight Pod check: FAILED")
|
||||
qp.P.LogVerboseMessage("Preflight Pod check: FAILED\n")
|
||||
return err
|
||||
}
|
||||
fmt.Println("Completed preflight pod check")
|
||||
qp.P.LogVerboseMessage("Completed preflight pod check\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qp *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namespace string) error {
|
||||
// create a pod
|
||||
podName := "pod-pf-check"
|
||||
pod, err := createPreflightTestPod(clientset, namespace, podName, qp.GetPreflightConfigObj().GetImageName(nginx))
|
||||
commandToRun := []string{}
|
||||
|
||||
imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create pod %s - %v\n", podName, err)
|
||||
return err
|
||||
}
|
||||
defer deletePod(clientset, namespace, podName)
|
||||
pod, err := qp.createPreflightTestPod(clientset, namespace, podName, imageName, nil, commandToRun)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create pod - %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer qp.deletePod(clientset, namespace, podName)
|
||||
|
||||
if err := waitForPod(clientset, namespace, pod); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Preflight pod creation check: PASSED")
|
||||
fmt.Println("Cleaning up resources...")
|
||||
qp.P.LogVerboseMessage("Preflight pod creation check: PASSED\n")
|
||||
qp.P.LogVerboseMessage("Cleaning up resources...\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkPfService(clientset *kubernetes.Clientset, namespace string) error {
|
||||
func (qp *QliksensePreflight) checkPfService(clientset *kubernetes.Clientset, namespace string) error {
|
||||
// creating service
|
||||
serviceName := "svc-pf-check"
|
||||
pfService, err := createPreflightTestService(clientset, namespace, serviceName)
|
||||
pfService, err := qp.createPreflightTestService(clientset, namespace, serviceName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create service : %s\n", serviceName)
|
||||
err = fmt.Errorf("unable to create service - %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer deleteService(clientset, namespace, serviceName)
|
||||
defer qp.deleteService(clientset, namespace, serviceName)
|
||||
_, err = getService(clientset, namespace, pfService.GetName())
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to retrieve service: %s\n", serviceName)
|
||||
err = fmt.Errorf("unable to retrieve service - %v\n", err)
|
||||
return err
|
||||
}
|
||||
fmt.Println("Preflight service creation check: PASSED")
|
||||
fmt.Println("Cleaning up resources...")
|
||||
qp.P.LogVerboseMessage("Preflight service creation check: PASSED\n")
|
||||
qp.P.LogVerboseMessage("Cleaning up resources...\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qp *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset, namespace, depName string) error {
|
||||
// check if we are able to create a deployment
|
||||
pfDeployment, err := createPreflightTestDeployment(clientset, namespace, depName, qp.GetPreflightConfigObj().GetImageName(nginx))
|
||||
imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create deployment: %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer deleteDeployment(clientset, namespace, depName)
|
||||
pfDeployment, err := qp.createPreflightTestDeployment(clientset, namespace, depName, imageName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create deployment - %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer qp.deleteDeployment(clientset, namespace, depName)
|
||||
if err := waitForDeployment(clientset, namespace, pfDeployment); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Preflight Deployment check: PASSED")
|
||||
fmt.Println("Cleaning up resources...")
|
||||
qp.P.LogVerboseMessage("Preflight Deployment check: PASSED\n")
|
||||
qp.P.LogVerboseMessage("Cleaning up resources...\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ package preflight
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -13,22 +11,26 @@ const (
|
||||
)
|
||||
|
||||
func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byte) error {
|
||||
clientset, clientConfig, err := getK8SClientSet(kubeConfigContents, "")
|
||||
qp.P.LogVerboseMessage("Preflight DNS check: \n")
|
||||
qp.P.LogVerboseMessage("------------------- \n")
|
||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// creating deployment
|
||||
depName := "dep-dns-preflight-check"
|
||||
dnsDeployment, err := createPreflightTestDeployment(clientset, namespace, depName, qp.GetPreflightConfigObj().GetImageName(nginx))
|
||||
nginxImageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create deployment: %v\n", err)
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
defer deleteDeployment(clientset, namespace, depName)
|
||||
dnsDeployment, err := qp.createPreflightTestDeployment(clientset, namespace, depName, nginxImageName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create deployment: %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer qp.deleteDeployment(clientset, namespace, depName)
|
||||
|
||||
if err := waitForDeployment(clientset, namespace, dnsDeployment); err != nil {
|
||||
return err
|
||||
@@ -36,46 +38,54 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by
|
||||
|
||||
// creating service
|
||||
serviceName := "svc-dns-pf-check"
|
||||
dnsService, err := createPreflightTestService(clientset, namespace, serviceName)
|
||||
dnsService, err := qp.createPreflightTestService(clientset, namespace, serviceName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create service : %s\n", serviceName)
|
||||
err = fmt.Errorf("unable to create service : %s, %s\n", serviceName, err)
|
||||
return err
|
||||
}
|
||||
defer deleteService(clientset, namespace, serviceName)
|
||||
defer qp.deleteService(clientset, namespace, serviceName)
|
||||
|
||||
// create a pod
|
||||
podName := "pf-pod-1"
|
||||
dnsPod, err := createPreflightTestPod(clientset, namespace, podName, qp.GetPreflightConfigObj().GetImageName(netcat))
|
||||
commandToRun := []string{"sh", "-c", "sleep 10; nc -z -v -w 1 " + dnsService.Name + " 80"}
|
||||
netcatImageName, err := qp.GetPreflightConfigObj().GetImageName(netcat, true)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create pod : %s\n", podName)
|
||||
err = fmt.Errorf("unable to retrieve image : %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer deletePod(clientset, namespace, podName)
|
||||
dnsPod, err := qp.createPreflightTestPod(clientset, namespace, podName, netcatImageName, nil, commandToRun)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create pod : %s, %s\n", podName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer qp.deletePod(clientset, namespace, podName)
|
||||
|
||||
if err := waitForPod(clientset, namespace, dnsPod); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(dnsPod.Spec.Containers) == 0 {
|
||||
err := fmt.Errorf("error: there are no containers in the pod")
|
||||
fmt.Println(err)
|
||||
err := fmt.Errorf("there are no containers in the pod")
|
||||
return err
|
||||
}
|
||||
api.LogDebugMessage("Exec-ing into the container...")
|
||||
stdout, stderr, err := executeRemoteCommand(clientset, clientConfig, dnsPod.Name, dnsPod.Spec.Containers[0].Name, namespace, []string{"nc", "-z", "-v", "-w 1", dnsService.Name, "80"})
|
||||
|
||||
waitForPodToDie(clientset, namespace, dnsPod)
|
||||
|
||||
logStr, err := getPodLogs(clientset, dnsPod)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to execute dns check in the cluster: %v", err)
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("unable to execute dns check in the cluster: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.HasSuffix(stdout, "succeeded!") || strings.HasSuffix(stderr, "succeeded!") {
|
||||
fmt.Println("Preflight DNS check: PASSED")
|
||||
if strings.HasSuffix(strings.TrimSpace(logStr), "succeeded!") {
|
||||
qp.P.LogVerboseMessage("Preflight DNS check: PASSED\n")
|
||||
} else {
|
||||
fmt.Println("Preflight DNS check: FAILED")
|
||||
err = fmt.Errorf("Expected response not found\n")
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Completed preflight DNS check")
|
||||
fmt.Println("Cleaning up resources...")
|
||||
qp.P.LogVerboseMessage("Completed preflight DNS check\n")
|
||||
qp.P.LogVerboseMessage("Cleaning up resources...\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,22 +2,26 @@ package preflight
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
const (
|
||||
mongoImage = "mongo"
|
||||
mongo = "mongo"
|
||||
)
|
||||
|
||||
func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace, mongodbUrl string) error {
|
||||
fmt.Printf("Preflight mongodb check: \n")
|
||||
func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions) error {
|
||||
qp.P.LogVerboseMessage("Preflight mongodb check: \n")
|
||||
qp.P.LogVerboseMessage("------------------------ \n")
|
||||
|
||||
if mongodbUrl == "" {
|
||||
if preflightOpts.MongoOptions.MongodbUrl == "" {
|
||||
// infer mongoDbUrl from currentCR
|
||||
fmt.Println("MongoDbUri is empty, infer from CR")
|
||||
qp.P.LogVerboseMessage("MongoDbUri is empty, infer from CR\n")
|
||||
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
|
||||
var currentCR *qapi.QliksenseCR
|
||||
|
||||
@@ -25,61 +29,131 @@ func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace, m
|
||||
qConfig.SetNamespace(namespace)
|
||||
currentCR, err = qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to retrieve current CR: %v\n", err)
|
||||
qp.P.LogVerboseMessage("Unable to retrieve current CR: %v\n", err)
|
||||
return err
|
||||
}
|
||||
decryptedCR, err := qConfig.GetDecryptedCr(currentCR)
|
||||
mongodbUrl = decryptedCR.Spec.GetFromSecrets("qliksense", "mongoDbUri")
|
||||
if err != nil {
|
||||
qp.P.LogVerboseMessage("An error occurred while retrieving mongodbUrl from current CR: %v\n", err)
|
||||
return err
|
||||
}
|
||||
preflightOpts.MongoOptions.MongodbUrl = decryptedCR.Spec.GetFromSecrets("qliksense", "mongoDbUri")
|
||||
}
|
||||
|
||||
fmt.Printf("mongodbUrl: %s\n", mongodbUrl)
|
||||
if err := mongoConnCheck(kubeConfigContents, namespace, mongodbUrl); err != nil {
|
||||
qp.P.LogVerboseMessage("MongodbUrl: %s\n", preflightOpts.MongoOptions.MongodbUrl)
|
||||
if err := qp.mongoConnCheck(kubeConfigContents, namespace, preflightOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Completed preflight mongodb check")
|
||||
qp.P.LogVerboseMessage("Completed preflight mongodb check\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func mongoConnCheck(kubeConfigContents []byte, namespace, mongodbUrl string) error {
|
||||
clientset, clientConfig, err := getK8SClientSet(kubeConfigContents, "")
|
||||
func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions) error {
|
||||
var caCertSecretName, clientCertSecretName string
|
||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
|
||||
return err
|
||||
}
|
||||
var secrets []string
|
||||
if preflightOpts.MongoOptions.CaCertFile != "" {
|
||||
caCertSecretName = "preflight-mongo-test-cacert"
|
||||
caCertSecret, err := qp.createSecret(clientset, namespace, preflightOpts.MongoOptions.CaCertFile, caCertSecretName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create a ca cert kubernetes secret: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer qp.deleteK8sSecret(clientset, namespace, caCertSecret)
|
||||
secrets = append(secrets, caCertSecretName)
|
||||
}
|
||||
if preflightOpts.MongoOptions.ClientCertFile != "" {
|
||||
clientCertSecretName = "preflight-mongo-test-clientcert"
|
||||
clientCertSecret, err := qp.createSecret(clientset, namespace, preflightOpts.MongoOptions.ClientCertFile, clientCertSecretName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create a client cert kubernetes secret: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer qp.deleteK8sSecret(clientset, namespace, clientCertSecret)
|
||||
secrets = append(secrets, clientCertSecretName)
|
||||
}
|
||||
|
||||
mongoCommand := strings.Builder{}
|
||||
mongoCommand.WriteString(fmt.Sprintf("sleep 10;mongo %s", preflightOpts.MongoOptions.MongodbUrl))
|
||||
if preflightOpts.MongoOptions.Username != "" {
|
||||
mongoCommand.WriteString(fmt.Sprintf(" --username %s", preflightOpts.MongoOptions.Username))
|
||||
api.LogDebugMessage("Adding username: Mongo command: %s\n", mongoCommand.String())
|
||||
}
|
||||
if preflightOpts.MongoOptions.Password != "" {
|
||||
mongoCommand.WriteString(fmt.Sprintf(" --password %s", preflightOpts.MongoOptions.Password))
|
||||
api.LogDebugMessage("Adding username and password\n")
|
||||
}
|
||||
if preflightOpts.MongoOptions.Tls || preflightOpts.MongoOptions.CaCertFile != "" || preflightOpts.MongoOptions.ClientCertFile != "" {
|
||||
mongoCommand.WriteString(" --tls")
|
||||
api.LogDebugMessage("Adding --tls: Mongo command: %s\n", mongoCommand.String())
|
||||
}
|
||||
if preflightOpts.MongoOptions.CaCertFile != "" {
|
||||
mongoCommand.WriteString(fmt.Sprintf(" --tlsCAFile=/etc/ssl/%s/%[1]s", caCertSecretName))
|
||||
api.LogDebugMessage("Adding caCertFile: Mongo command: %s\n", mongoCommand.String())
|
||||
}
|
||||
if preflightOpts.MongoOptions.ClientCertFile != "" {
|
||||
mongoCommand.WriteString(fmt.Sprintf(" --tlsCertificateKeyFile=/etc/ssl/%s/%[1]s", clientCertSecretName))
|
||||
api.LogDebugMessage("Adding clientCertFile: Mongo command: %s\n", mongoCommand.String())
|
||||
}
|
||||
mongoCommand.WriteString(` --eval "print(\"connected to mongo\")"`)
|
||||
|
||||
commandToRun := []string{"sh", "-c", mongoCommand.String()}
|
||||
api.LogDebugMessage("Mongo command: %s\n", strings.Join(commandToRun, " "))
|
||||
|
||||
// create a pod
|
||||
podName := "pf-mongo-pod"
|
||||
mongoPod, err := createPreflightTestPod(clientset, namespace, podName, mongoImage)
|
||||
imageName, err := qp.GetPreflightConfigObj().GetImageName(mongo, true)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create pod : %s\n", podName)
|
||||
fmt.Println("Preflight mongo check: FAILED")
|
||||
err = fmt.Errorf("unable to retrieve image : %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer deletePod(clientset, namespace, podName)
|
||||
mongoPod, err := qp.createPreflightTestPod(clientset, namespace, podName, imageName, secrets, commandToRun)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create pod : %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer qp.deletePod(clientset, namespace, podName)
|
||||
|
||||
if err := waitForPod(clientset, namespace, mongoPod); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(mongoPod.Spec.Containers) == 0 {
|
||||
err := fmt.Errorf("error: there are no containers in the pod")
|
||||
fmt.Println(err)
|
||||
err := fmt.Errorf("there are no containers in the pod- %v\n", err)
|
||||
return err
|
||||
}
|
||||
api.LogDebugMessage("Exec-ing into the container...")
|
||||
stdout, stderr, err := executeRemoteCommand(clientset, clientConfig, mongoPod.Name, mongoPod.Spec.Containers[0].Name, namespace, []string{"mongo", mongodbUrl})
|
||||
waitForPodToDie(clientset, namespace, mongoPod)
|
||||
logStr, err := getPodLogs(clientset, mongoPod)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to execute mongo check in the cluster: %v", err)
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("unable to execute mongo check in the cluster: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("stdout:", stdout)
|
||||
fmt.Println("stderr:", stderr)
|
||||
stringToCheck := "Implicit session"
|
||||
if strings.Contains(stdout, stringToCheck) || strings.Contains(stderr, stringToCheck) {
|
||||
fmt.Println("Preflight mongo check: PASSED")
|
||||
stringToCheck := "Implicit session:"
|
||||
if strings.Contains(logStr, stringToCheck) {
|
||||
qp.P.LogVerboseMessage("Preflight mongo check: PASSED\n")
|
||||
} else {
|
||||
fmt.Println("Preflight mongo check: FAILED")
|
||||
err = fmt.Errorf("Connection failed: %s\n", logStr)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qp *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, namespace, certFile, certSecretName string) (*apiv1.Secret, error) {
|
||||
certBytes, err := ioutil.ReadFile(certFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certSecret, err := qp.createPreflightTestSecret(clientset, namespace, certSecretName, certBytes)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create secret with ca cert : %v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
return certSecret, nil
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package preflight
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
api "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func Test_Initalize(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
pf := NewPreflightConfig(tempDir)
|
||||
if err := pf.Initialize(); err != nil {
|
||||
t.Log()
|
||||
t.FailNow()
|
||||
}
|
||||
p := &PreflightConfig{
|
||||
QliksenseHomePath: tempDir,
|
||||
}
|
||||
if err := api.ReadFromFile(p, pf.GetConfigFilePath()); err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if p.GetMinK8sVersion() != "1.15" {
|
||||
t.Log("expected k8 version: 1.15, but got " + p.GetMinK8sVersion())
|
||||
t.Fail()
|
||||
}
|
||||
p.AddImage("test", "testimage")
|
||||
if err := p.Write(); err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
p2 := NewPreflightConfig(tempDir)
|
||||
if p2.GetImageName("test") != "testimage" {
|
||||
t.Log("expected image name: testimage, got: " + p2.GetImageName("test"))
|
||||
}
|
||||
}
|
||||
@@ -6,43 +6,57 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
"k8s.io/client-go/util/retry"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/api/rbac/v1beta1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/util/retry"
|
||||
)
|
||||
|
||||
type PreflightOptions struct {
|
||||
Verbose bool
|
||||
MongoOptions *MongoOptions
|
||||
}
|
||||
|
||||
// LogVerboseMessage logs a verbose message
|
||||
func (p *PreflightOptions) LogVerboseMessage(strMessage string, args ...interface{}) {
|
||||
if p.Verbose || os.Getenv("QLIKSENSE_DEBUG") == "true" {
|
||||
fmt.Printf(strMessage, args...)
|
||||
}
|
||||
}
|
||||
|
||||
type MongoOptions struct {
|
||||
MongodbUrl string
|
||||
Username string
|
||||
Password string
|
||||
CaCertFile string
|
||||
ClientCertFile string
|
||||
Tls bool
|
||||
}
|
||||
|
||||
var gracePeriod int64 = 0
|
||||
|
||||
type QliksensePreflight struct {
|
||||
Q *qliksense.Qliksense
|
||||
P *PreflightOptions
|
||||
}
|
||||
|
||||
func (qp *QliksensePreflight) GetPreflightConfigObj() *PreflightConfig {
|
||||
return NewPreflightConfig(qp.Q.QliksenseHome)
|
||||
func (qp *QliksensePreflight) GetPreflightConfigObj() *api.PreflightConfig {
|
||||
return api.NewPreflightConfig(qp.Q.QliksenseHome)
|
||||
}
|
||||
|
||||
func InitPreflight() (string, []byte, error) {
|
||||
@@ -102,14 +116,12 @@ func getK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clients
|
||||
clientConfig, err = rest.InClusterConfig()
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "Unable to load in-cluster kubeconfig")
|
||||
fmt.Println(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
config, err := clientcmd.Load(kubeconfig)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "Unable to load kubeconfig")
|
||||
fmt.Println(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
if contextName != "" {
|
||||
@@ -118,20 +130,18 @@ func getK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clients
|
||||
clientConfig, err = clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig()
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "Unable to create client config from config")
|
||||
fmt.Println(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
clientset, err := kubernetes.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "Unable to create clientset")
|
||||
fmt.Println(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
return clientset, clientConfig, nil
|
||||
}
|
||||
|
||||
func createPreflightTestDeployment(clientset *kubernetes.Clientset, namespace string, depName string, imageName string) (*appsv1.Deployment, error) {
|
||||
func (qp *QliksensePreflight) createPreflightTestDeployment(clientset *kubernetes.Clientset, namespace string, depName string, imageName string) (*appsv1.Deployment, error) {
|
||||
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
||||
deployment := &appsv1.Deployment{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -176,11 +186,10 @@ func createPreflightTestDeployment(clientset *kubernetes.Clientset, namespace st
|
||||
result, err = deploymentsClient.Create(deployment)
|
||||
return err
|
||||
}); err != nil {
|
||||
err = errors.Wrapf(err, "error: unable to create deployments in the %s namespace", namespace)
|
||||
fmt.Println(err)
|
||||
err = errors.Wrapf(err, "unable to create deployments in the %s namespace", namespace)
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("Created deployment %q\n", result.GetObjectMeta().GetName())
|
||||
qp.P.LogVerboseMessage("Created deployment %q\n", result.GetObjectMeta().GetName())
|
||||
|
||||
return deployment, nil
|
||||
}
|
||||
@@ -192,14 +201,14 @@ func getDeployment(clientset *kubernetes.Clientset, namespace, depName string) (
|
||||
deployment, err = deploymentsClient.Get(depName, v1.GetOptions{})
|
||||
return err
|
||||
}); err != nil {
|
||||
err = errors.Wrapf(err, "error: unable to get deployments in the %s namespace", namespace)
|
||||
err = errors.Wrapf(err, "unable to get deployments in the %s namespace", namespace)
|
||||
api.LogDebugMessage("%v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
return deployment, nil
|
||||
}
|
||||
|
||||
func deleteDeployment(clientset *kubernetes.Clientset, namespace, name string) error {
|
||||
func (qp *QliksensePreflight) deleteDeployment(clientset *kubernetes.Clientset, namespace, name string) error {
|
||||
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
||||
// Create Deployment
|
||||
deletePolicy := v1.DeletePropagationForeground
|
||||
@@ -211,17 +220,16 @@ func deleteDeployment(clientset *kubernetes.Clientset, namespace, name string) e
|
||||
if err := retryOnError(func() (err error) {
|
||||
return deploymentsClient.Delete(name, &deleteOptions)
|
||||
}); err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
if err := waitForDeploymentToDelete(clientset, namespace, name); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Deleted deployment: %s\n", name)
|
||||
qp.P.LogVerboseMessage("Deleted deployment: %s\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func createPreflightTestService(clientset *kubernetes.Clientset, namespace string, svcName string) (*apiv1.Service, error) {
|
||||
func (qp *QliksensePreflight) createPreflightTestService(clientset *kubernetes.Clientset, namespace string, svcName string) (*apiv1.Service, error) {
|
||||
iptr := int32Ptr(80)
|
||||
servicesClient := clientset.CoreV1().Services(namespace)
|
||||
service := &apiv1.Service{
|
||||
@@ -249,10 +257,9 @@ func createPreflightTestService(clientset *kubernetes.Clientset, namespace strin
|
||||
result, err = servicesClient.Create(service)
|
||||
return err
|
||||
}); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("Created service %q\n", result.GetObjectMeta().GetName())
|
||||
qp.P.LogVerboseMessage("Created service %q\n", result.GetObjectMeta().GetName())
|
||||
|
||||
return service, nil
|
||||
}
|
||||
@@ -265,14 +272,13 @@ func getService(clientset *kubernetes.Clientset, namespace, svcName string) (*ap
|
||||
return err
|
||||
}); err != nil {
|
||||
err = errors.Wrapf(err, "unable to get services in the %s namespace", namespace)
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
func deleteService(clientset *kubernetes.Clientset, namespace, name string) error {
|
||||
func (qp *QliksensePreflight) deleteService(clientset *kubernetes.Clientset, namespace, name string) error {
|
||||
servicesClient := clientset.CoreV1().Services(namespace)
|
||||
// Create Deployment
|
||||
deletePolicy := v1.DeletePropagationForeground
|
||||
@@ -285,11 +291,11 @@ func deleteService(clientset *kubernetes.Clientset, namespace, name string) erro
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Deleted service: %s\n", name)
|
||||
qp.P.LogVerboseMessage("Deleted service: %s\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func deletePod(clientset *kubernetes.Clientset, namespace, name string) error {
|
||||
func (qp *QliksensePreflight) deletePod(clientset *kubernetes.Clientset, namespace, name string) error {
|
||||
|
||||
podsClient := clientset.CoreV1().Pods(namespace)
|
||||
deletePolicy := v1.DeletePropagationForeground
|
||||
@@ -300,17 +306,16 @@ func deletePod(clientset *kubernetes.Clientset, namespace, name string) error {
|
||||
if err := retryOnError(func() (err error) {
|
||||
return podsClient.Delete(name, &deleteOptions)
|
||||
}); err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
if err := waitForPodToDelete(clientset, namespace, name); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Deleted pod: %s\n", name)
|
||||
qp.P.LogVerboseMessage("Deleted pod: %s\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func createPreflightTestPod(clientset *kubernetes.Clientset, namespace string, podName string, imageName string) (*apiv1.Pod, error) {
|
||||
func (qp *QliksensePreflight) createPreflightTestPod(clientset *kubernetes.Clientset, namespace, podName, imageName string, secretNames []string, commandToRun []string) (*apiv1.Pod, error) {
|
||||
// build the pod definition we want to deploy
|
||||
pod := &apiv1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
@@ -321,29 +326,51 @@ func createPreflightTestPod(clientset *kubernetes.Clientset, namespace string, p
|
||||
},
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
RestartPolicy: apiv1.RestartPolicyNever,
|
||||
Containers: []apiv1.Container{
|
||||
{
|
||||
Name: "cnt",
|
||||
Image: imageName,
|
||||
ImagePullPolicy: apiv1.PullIfNotPresent,
|
||||
Command: []string{
|
||||
"sleep",
|
||||
"3600",
|
||||
},
|
||||
Command: commandToRun,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if len(secretNames) > 0 {
|
||||
for _, secretName := range secretNames {
|
||||
pod.Spec.Volumes = append(pod.Spec.Volumes, apiv1.Volume{
|
||||
Name: secretName,
|
||||
VolumeSource: apiv1.VolumeSource{
|
||||
Secret: &apiv1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
Items: []apiv1.KeyToPath{
|
||||
{
|
||||
Key: secretName,
|
||||
Path: secretName,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if len(pod.Spec.Containers) > 0 {
|
||||
pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, apiv1.VolumeMount{
|
||||
Name: secretName,
|
||||
MountPath: "/etc/ssl/" + secretName,
|
||||
ReadOnly: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now create the pod in kubernetes cluster using the clientset
|
||||
if err := retryOnError(func() (err error) {
|
||||
pod, err = clientset.CoreV1().Pods(namespace).Create(pod)
|
||||
return err
|
||||
}); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("Created pod: %s\n", pod.Name)
|
||||
qp.P.LogVerboseMessage("Created pod: %s\n", pod.Name)
|
||||
return pod, nil
|
||||
}
|
||||
|
||||
@@ -360,67 +387,68 @@ func getPod(clientset *kubernetes.Clientset, namespace, podName string) (*apiv1.
|
||||
return pod, nil
|
||||
}
|
||||
|
||||
func execute(method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error {
|
||||
exec, err := remotecommand.NewSPDYExecutor(config, method, url)
|
||||
func getPodLogs(clientset *kubernetes.Clientset, pod *apiv1.Pod) (string, error) {
|
||||
podLogOpts := apiv1.PodLogOptions{}
|
||||
|
||||
api.LogDebugMessage("Retrieving logs for pod: %s namespace: %s\n", pod.GetName(), pod.Namespace)
|
||||
req := clientset.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOpts)
|
||||
podLogs, err := req.Stream()
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
return exec.Stream(remotecommand.StreamOptions{
|
||||
Stdin: stdin,
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
Tty: tty,
|
||||
})
|
||||
defer podLogs.Close()
|
||||
time.Sleep(15 * time.Second)
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = io.Copy(buf, podLogs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
api.LogDebugMessage("Log from pod: %s\n", buf.String())
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func executeRemoteCommand(clientset *kubernetes.Clientset, config *rest.Config, podName, containerName, namespace string, command []string) (string, string, error) {
|
||||
tty := false
|
||||
req := clientset.CoreV1().RESTClient().Post().
|
||||
Resource("pods").
|
||||
Name(podName).
|
||||
Namespace(namespace).
|
||||
SubResource("exec").
|
||||
Param("container", containerName)
|
||||
req.VersionedParams(&apiv1.PodExecOptions{
|
||||
Container: containerName,
|
||||
Command: command,
|
||||
Stdin: false,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
TTY: tty,
|
||||
}, scheme.ParameterCodec)
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
err := execute("POST", req.URL(), config, nil, &stdout, &stderr, tty)
|
||||
return strings.TrimSpace(stdout.String()), strings.TrimSpace(stderr.String()), err
|
||||
}
|
||||
|
||||
func waitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDeployment *appsv1.Deployment) error {
|
||||
func waitForResource(checkFunc func() (interface{}, error), validateFunc func(interface{}) bool) error {
|
||||
timeout := time.NewTicker(2 * time.Minute)
|
||||
defer timeout.Stop()
|
||||
var d *appsv1.Deployment
|
||||
var err error
|
||||
WAIT:
|
||||
OUT:
|
||||
for {
|
||||
d, err = getDeployment(clientset, namespace, pfDeployment.GetName())
|
||||
r, err := checkFunc()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to retrieve deployment: %s\n", pfDeployment.GetName())
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-timeout.C:
|
||||
break WAIT
|
||||
break OUT
|
||||
default:
|
||||
if int(d.Status.ReadyReplicas) > 0 {
|
||||
break WAIT
|
||||
if validateFunc(r) {
|
||||
break OUT
|
||||
}
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
if int(d.Status.ReadyReplicas) == 0 {
|
||||
err = fmt.Errorf("error: deployment took longer than expected to spin up pods")
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
func waitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDeployment *appsv1.Deployment) error {
|
||||
var err error
|
||||
depName := pfDeployment.GetName()
|
||||
checkFunc := func() (interface{}, error) {
|
||||
pfDeployment, err = getDeployment(clientset, namespace, depName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to retrieve deployment: %s\n", depName)
|
||||
return nil, err
|
||||
}
|
||||
return pfDeployment, nil
|
||||
}
|
||||
validateFunc := func(data interface{}) bool {
|
||||
d := data.(*appsv1.Deployment)
|
||||
return int(d.Status.ReadyReplicas) > 0
|
||||
}
|
||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||
return err
|
||||
}
|
||||
if int(pfDeployment.Status.ReadyReplicas) == 0 {
|
||||
err = fmt.Errorf("deployment took longer than expected to spin up pods")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -428,87 +456,92 @@ WAIT:
|
||||
|
||||
func waitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error {
|
||||
var err error
|
||||
if len(pod.Spec.Containers) > 0 {
|
||||
timeout := time.NewTicker(2 * time.Minute)
|
||||
defer timeout.Stop()
|
||||
podName := pod.Name
|
||||
OUT:
|
||||
for {
|
||||
pod, err = getPod(clientset, namespace, podName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to retrieve %s pod by name", podName)
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-timeout.C:
|
||||
break OUT
|
||||
default:
|
||||
if len(pod.Status.ContainerStatuses) > 0 && pod.Status.ContainerStatuses[0].Ready {
|
||||
break OUT
|
||||
}
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
if len(pod.Status.ContainerStatuses) == 0 || !pod.Status.ContainerStatuses[0].Ready {
|
||||
err = fmt.Errorf("error: container is taking much longer than expected")
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
if len(pod.Spec.Containers) == 0 {
|
||||
err = fmt.Errorf("there are no containers in the pod")
|
||||
return err
|
||||
}
|
||||
err = fmt.Errorf("error: there are no containers in the pod")
|
||||
fmt.Println(err)
|
||||
return err
|
||||
podName := pod.Name
|
||||
checkFunc := func() (interface{}, error) {
|
||||
pod, err = getPod(clientset, namespace, podName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to retrieve %s pod by name", podName)
|
||||
return nil, err
|
||||
}
|
||||
return pod, nil
|
||||
}
|
||||
validateFunc := func(data interface{}) bool {
|
||||
po := data.(*apiv1.Pod)
|
||||
return len(po.Status.ContainerStatuses) > 0 && po.Status.ContainerStatuses[0].Ready
|
||||
}
|
||||
|
||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(pod.Status.ContainerStatuses) == 0 || !pod.Status.ContainerStatuses[0].Ready {
|
||||
err = fmt.Errorf("container is taking much longer than expected")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func waitForPodToDie(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error {
|
||||
podName := pod.Name
|
||||
checkFunc := func() (interface{}, error) {
|
||||
po, err := getPod(clientset, namespace, podName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to retrieve %s pod by name", podName)
|
||||
return nil, err
|
||||
}
|
||||
api.LogDebugMessage("pod status: %v\n", po.Status.Phase)
|
||||
return po, nil
|
||||
}
|
||||
validateFunc := func(r interface{}) bool {
|
||||
po := r.(*apiv1.Pod)
|
||||
return po.Status.Phase == apiv1.PodFailed || po.Status.Phase == apiv1.PodSucceeded
|
||||
}
|
||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func waitForPodToDelete(clientset *kubernetes.Clientset, namespace, podName string) error {
|
||||
var err error
|
||||
timeout := time.NewTicker(2 * time.Minute)
|
||||
defer timeout.Stop()
|
||||
OUT:
|
||||
for {
|
||||
_, err = getPod(clientset, namespace, podName)
|
||||
checkFunc := func() (interface{}, error) {
|
||||
po, err := getPod(clientset, namespace, podName)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
select {
|
||||
case <-timeout.C:
|
||||
break OUT
|
||||
default:
|
||||
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
return po, nil
|
||||
}
|
||||
err = fmt.Errorf("error: delete pod is taking unusually long")
|
||||
fmt.Println(err)
|
||||
validateFunc := func(po interface{}) bool {
|
||||
return false
|
||||
}
|
||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||
return nil
|
||||
}
|
||||
err := fmt.Errorf("delete pod is taking unusually long")
|
||||
return err
|
||||
}
|
||||
|
||||
func waitForDeploymentToDelete(clientset *kubernetes.Clientset, namespace, deploymentName string) error {
|
||||
var err error
|
||||
timeout := time.NewTicker(2 * time.Minute)
|
||||
defer timeout.Stop()
|
||||
OUT:
|
||||
for {
|
||||
_, err = getDeployment(clientset, namespace, deploymentName)
|
||||
checkFunc := func() (interface{}, error) {
|
||||
dep, err := getDeployment(clientset, namespace, deploymentName)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
select {
|
||||
case <-timeout.C:
|
||||
break OUT
|
||||
default:
|
||||
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
return dep, nil
|
||||
}
|
||||
err = fmt.Errorf("error: delete deployment is taking unusually long")
|
||||
fmt.Println(err)
|
||||
validateFunc := func(po interface{}) bool {
|
||||
return false
|
||||
}
|
||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||
return nil
|
||||
}
|
||||
err := fmt.Errorf("delete deployment is taking unusually long")
|
||||
return err
|
||||
}
|
||||
|
||||
func createPfRole(clientset *kubernetes.Clientset, namespace, roleName string) (*v1beta1.Role, error) {
|
||||
func (qp *QliksensePreflight) createPfRole(clientset *kubernetes.Clientset, namespace, roleName string) (*v1beta1.Role, error) {
|
||||
// build the role defination we want to create
|
||||
var role *v1beta1.Role
|
||||
roleSpec := &v1beta1.Role{
|
||||
@@ -527,16 +560,15 @@ func createPfRole(clientset *kubernetes.Clientset, namespace, roleName string) (
|
||||
role, err = clientset.RbacV1beta1().Roles(namespace).Create(roleSpec)
|
||||
return err
|
||||
}); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("Created role: %s\n", role.Name)
|
||||
qp.P.LogVerboseMessage("Created role: %s\n", role.Name)
|
||||
|
||||
return role, nil
|
||||
}
|
||||
|
||||
func deleteRole(clientset *kubernetes.Clientset, namespace string, role *v1beta1.Role) {
|
||||
func (qp *QliksensePreflight) deleteRole(clientset *kubernetes.Clientset, namespace string, role *v1beta1.Role) {
|
||||
rolesClient := clientset.RbacV1beta1().Roles(namespace)
|
||||
|
||||
deletePolicy := v1.DeletePropagationForeground
|
||||
@@ -547,10 +579,10 @@ func deleteRole(clientset *kubernetes.Clientset, namespace string, role *v1beta1
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("Deleted role: %s\n\n", role.Name)
|
||||
qp.P.LogVerboseMessage("Deleted role: %s\n\n", role.Name)
|
||||
}
|
||||
|
||||
func createPfRoleBinding(clientset *kubernetes.Clientset, namespace, roleBindingName string) (*v1beta1.RoleBinding, error) {
|
||||
func (qp *QliksensePreflight) createPfRoleBinding(clientset *kubernetes.Clientset, namespace, roleBindingName string) (*v1beta1.RoleBinding, error) {
|
||||
var roleBinding *v1beta1.RoleBinding
|
||||
// build the rolebinding defination we want to create
|
||||
roleBindingSpec := &v1beta1.RoleBinding{
|
||||
@@ -558,7 +590,7 @@ func createPfRoleBinding(clientset *kubernetes.Clientset, namespace, roleBinding
|
||||
Name: roleBindingName,
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
"app": "demo",
|
||||
"app": "preflight",
|
||||
},
|
||||
},
|
||||
Subjects: []v1beta1.Subject{
|
||||
@@ -581,14 +613,13 @@ func createPfRoleBinding(clientset *kubernetes.Clientset, namespace, roleBinding
|
||||
roleBinding, err = clientset.RbacV1beta1().RoleBindings(namespace).Create(roleBindingSpec)
|
||||
return err
|
||||
}); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("Created RoleBinding: %s\n", roleBindingSpec.Name)
|
||||
qp.P.LogVerboseMessage("Created RoleBinding: %s\n", roleBindingSpec.Name)
|
||||
return roleBinding, nil
|
||||
}
|
||||
|
||||
func deleteRoleBinding(clientset *kubernetes.Clientset, namespace string, roleBinding *v1beta1.RoleBinding) {
|
||||
func (qp *QliksensePreflight) deleteRoleBinding(clientset *kubernetes.Clientset, namespace string, roleBinding *v1beta1.RoleBinding) {
|
||||
roleBindingClient := clientset.RbacV1beta1().RoleBindings(namespace)
|
||||
|
||||
deletePolicy := v1.DeletePropagationForeground
|
||||
@@ -599,10 +630,10 @@ func deleteRoleBinding(clientset *kubernetes.Clientset, namespace string, roleBi
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("Deleted RoleBinding: %s\n\n", roleBinding.Name)
|
||||
qp.P.LogVerboseMessage("Deleted RoleBinding: %s\n\n", roleBinding.Name)
|
||||
}
|
||||
|
||||
func createPfServiceAccount(clientset *kubernetes.Clientset, namespace, serviceAccountName string) (*apiv1.ServiceAccount, error) {
|
||||
func (qp *QliksensePreflight) createPfServiceAccount(clientset *kubernetes.Clientset, namespace, serviceAccountName string) (*apiv1.ServiceAccount, error) {
|
||||
var serviceAccount *apiv1.ServiceAccount
|
||||
// build the serviceAccount defination we want to create
|
||||
serviceAccountSpec := &apiv1.ServiceAccount{
|
||||
@@ -610,7 +641,7 @@ func createPfServiceAccount(clientset *kubernetes.Clientset, namespace, serviceA
|
||||
Name: "preflight-check-test-serviceaccount",
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
"app": "demo",
|
||||
"app": "preflight",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -620,14 +651,13 @@ func createPfServiceAccount(clientset *kubernetes.Clientset, namespace, serviceA
|
||||
serviceAccount, err = clientset.CoreV1().ServiceAccounts(namespace).Create(serviceAccountSpec)
|
||||
return err
|
||||
}); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("Created Service Account: %s\n", serviceAccountSpec.Name)
|
||||
qp.P.LogVerboseMessage("Created Service Account: %s\n", serviceAccountSpec.Name)
|
||||
return serviceAccount, nil
|
||||
}
|
||||
|
||||
func deleteServiceAccount(clientset *kubernetes.Clientset, namespace string, serviceAccount *apiv1.ServiceAccount) {
|
||||
func (qp *QliksensePreflight) deleteServiceAccount(clientset *kubernetes.Clientset, namespace string, serviceAccount *apiv1.ServiceAccount) {
|
||||
serviceAccountClient := clientset.CoreV1().ServiceAccounts(namespace)
|
||||
|
||||
deletePolicy := v1.DeletePropagationForeground
|
||||
@@ -638,5 +668,47 @@ func deleteServiceAccount(clientset *kubernetes.Clientset, namespace string, ser
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("Deleted ServiceAccount: %s\n\n", serviceAccount.Name)
|
||||
qp.P.LogVerboseMessage("Deleted ServiceAccount: %s\n\n", serviceAccount.Name)
|
||||
}
|
||||
|
||||
func (qp *QliksensePreflight) createPreflightTestSecret(clientset *kubernetes.Clientset, namespace, secretName string, secretData []byte) (*apiv1.Secret, error) {
|
||||
var secret *apiv1.Secret
|
||||
var err error
|
||||
// build the secret defination we want to create
|
||||
secretSpec := &apiv1.Secret{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: secretName,
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
"app": "preflight",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
secretName: secretData,
|
||||
},
|
||||
}
|
||||
|
||||
// now create the secret in kubernetes cluster using the clientset
|
||||
if err = retryOnError(func() (err error) {
|
||||
secret, err = clientset.CoreV1().Secrets(namespace).Create(secretSpec)
|
||||
return err
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qp.P.LogVerboseMessage("Created Secret: %s\n", secret.Name)
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
func (qp *QliksensePreflight) deleteK8sSecret(clientset *kubernetes.Clientset, namespace string, k8sSecret *apiv1.Secret) {
|
||||
secretClient := clientset.CoreV1().Secrets(namespace)
|
||||
|
||||
deletePolicy := v1.DeletePropagationForeground
|
||||
deleteOptions := v1.DeleteOptions{
|
||||
PropagationPolicy: &deletePolicy,
|
||||
}
|
||||
err := secretClient.Delete(k8sSecret.GetName(), &deleteOptions)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
qp.P.LogVerboseMessage("Deleted Secret: %s\n", k8sSecret.Name)
|
||||
}
|
||||
|
||||
@@ -5,41 +5,47 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
)
|
||||
|
||||
var resultYamlBytes = []byte("")
|
||||
|
||||
func (qp *QliksensePreflight) CheckCreateRole(namespace string) error {
|
||||
// create a Role
|
||||
fmt.Printf("Preflight role check: \n")
|
||||
qp.P.LogVerboseMessage("Preflight role check: \n")
|
||||
qp.P.LogVerboseMessage("--------------------- \n")
|
||||
err := qp.checkCreateEntity(namespace, "Role")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Completed preflight role check")
|
||||
qp.P.LogVerboseMessage("Completed preflight role check\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qp *QliksensePreflight) CheckCreateRoleBinding(namespace string) error {
|
||||
// create a RoleBinding
|
||||
fmt.Printf("Preflight rolebinding check: \n")
|
||||
qp.P.LogVerboseMessage("Preflight rolebinding check: \n")
|
||||
qp.P.LogVerboseMessage("---------------------------- \n")
|
||||
err := qp.checkCreateEntity(namespace, "RoleBinding")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Completed preflight rolebinding check")
|
||||
qp.P.LogVerboseMessage("Completed preflight rolebinding check\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qp *QliksensePreflight) CheckCreateServiceAccount(namespace string) error {
|
||||
// create a service account
|
||||
fmt.Printf("Preflight serviceaccount check: \n")
|
||||
qp.P.LogVerboseMessage("Preflight serviceaccount check: \n")
|
||||
qp.P.LogVerboseMessage("------------------------------- \n")
|
||||
err := qp.checkCreateEntity(namespace, "ServiceAccount")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Completed preflight serviceaccount check")
|
||||
qp.P.LogVerboseMessage("Completed preflight serviceaccount check\n")
|
||||
return nil
|
||||
}
|
||||
func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string) error {
|
||||
@@ -50,13 +56,13 @@ func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string)
|
||||
var err error
|
||||
currentCR, err = qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to retrieve current CR: %v\n", err)
|
||||
qp.P.LogVerboseMessage("Unable to retrieve current CR: %v\n", err)
|
||||
return err
|
||||
}
|
||||
if currentCR.IsRepoExist() {
|
||||
mfroot = currentCR.Spec.GetManifestsRoot()
|
||||
} else if tempDownloadedDir, err := qliksense.DownloadFromGitRepoToTmpDir(qliksense.QLIK_GIT_REPO, "master"); err != nil {
|
||||
fmt.Printf("Unable to Download from git repo to tmp dir: %v\n", err)
|
||||
qp.P.LogVerboseMessage("Unable to Download from git repo to tmp dir: %v\n", err)
|
||||
return err
|
||||
} else {
|
||||
mfroot = tempDownloadedDir
|
||||
@@ -67,65 +73,85 @@ func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string)
|
||||
} else {
|
||||
kusDir = filepath.Join(mfroot, "manifests", currentCR.Spec.Profile)
|
||||
}
|
||||
resultYamlString, err := qliksense.ExecuteKustomizeBuild(kusDir)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to retrieve manifests from executing kustomize: %v\n", err)
|
||||
return err
|
||||
if len(resultYamlBytes) == 0 {
|
||||
resultYamlBytes, err = qliksense.ExecuteKustomizeBuild(kusDir)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Unable to retrieve manifests from executing kustomize: %s, error: %v", kusDir, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sa := qliksense.GetYamlsFromMultiDoc(string(resultYamlString), entityToTest)
|
||||
sa := qliksense.GetYamlsFromMultiDoc(string(resultYamlBytes), entityToTest)
|
||||
if sa != "" {
|
||||
sa = strings.ReplaceAll(sa, "namespace: default\n", fmt.Sprintf("namespace: %s\n", namespace))
|
||||
sa = strings.Replace(sa, "name: qliksense", "name: preflight", -1)
|
||||
} else {
|
||||
err := fmt.Errorf("Unable to retrieve yamls to apply on cluster")
|
||||
fmt.Println(err)
|
||||
err := fmt.Errorf("Unable to retrieve yamls to apply on cluster from dir: %s, error: %v", kusDir, err)
|
||||
return err
|
||||
}
|
||||
namespace = "" // namespace is handled when generating the manifests
|
||||
|
||||
defer func() {
|
||||
qp.P.LogVerboseMessage("Cleaning up resources...\n")
|
||||
err := api.KubectlDeleteVerbose(sa, namespace, qp.P.Verbose)
|
||||
if err != nil {
|
||||
qp.P.LogVerboseMessage("Preflight cleanup failed!\n")
|
||||
}
|
||||
}()
|
||||
|
||||
err = api.KubectlApplyVerbose(sa, namespace, qp.P.Verbose)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Failed to create entity on the cluster: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = api.KubectlApply(sa, namespace)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Failed to create entity on the cluster")
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Preflight %s check: PASSED\n", entityToTest)
|
||||
fmt.Println("Cleaning up resources")
|
||||
err = api.KubectlDelete(sa, namespace)
|
||||
if err != nil {
|
||||
fmt.Println("Preflight cleanup failed!")
|
||||
return err
|
||||
}
|
||||
qp.P.LogVerboseMessage("Preflight %s check: PASSED\n", entityToTest)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qp *QliksensePreflight) CheckCreateRB(namespace string, kubeConfigContents []byte) error {
|
||||
|
||||
// create a role
|
||||
fmt.Printf("Preflight createRole check: \n")
|
||||
err := qp.checkCreateEntity(namespace, "Role")
|
||||
if err != nil {
|
||||
fmt.Println("Preflight role check: FAILED")
|
||||
qp.P.LogVerboseMessage("Preflight createRole check: \n")
|
||||
qp.P.LogVerboseMessage("--------------------------- \n")
|
||||
errStr := strings.Builder{}
|
||||
err1 := qp.checkCreateEntity(namespace, "Role")
|
||||
if err1 != nil {
|
||||
errStr.WriteString(err1.Error())
|
||||
errStr.WriteString("\n")
|
||||
qp.P.LogVerboseMessage("%v\n", err1)
|
||||
qp.P.LogVerboseMessage("Preflight role check: FAILED\n")
|
||||
}
|
||||
fmt.Printf("Completed preflight role check\n\n")
|
||||
qp.P.LogVerboseMessage("Completed preflight role check\n\n")
|
||||
|
||||
// create a roleBinding
|
||||
fmt.Printf("Preflight rolebinding check: \n")
|
||||
err = qp.checkCreateEntity(namespace, "RoleBinding")
|
||||
if err != nil {
|
||||
fmt.Println("Preflight rolebinding check: FAILED")
|
||||
qp.P.LogVerboseMessage("Preflight rolebinding check: \n")
|
||||
qp.P.LogVerboseMessage("---------------------------- \n")
|
||||
err2 := qp.checkCreateEntity(namespace, "RoleBinding")
|
||||
if err2 != nil {
|
||||
errStr.WriteString(err2.Error())
|
||||
errStr.WriteString("\n")
|
||||
qp.P.LogVerboseMessage("%v\n", err2)
|
||||
qp.P.LogVerboseMessage("Preflight rolebinding check: FAILED\n")
|
||||
}
|
||||
fmt.Printf("Completed preflight rolebinding check\n\n")
|
||||
qp.P.LogVerboseMessage("Completed preflight rolebinding check\n\n")
|
||||
|
||||
// create a service account
|
||||
fmt.Printf("Preflight serviceaccount check: \n")
|
||||
err = qp.checkCreateEntity(namespace, "ServiceAccount")
|
||||
if err != nil {
|
||||
fmt.Println("Preflight serviceaccount check: FAILED")
|
||||
qp.P.LogVerboseMessage("Preflight serviceaccount check: \n")
|
||||
qp.P.LogVerboseMessage("------------------------------- \n")
|
||||
err3 := qp.checkCreateEntity(namespace, "ServiceAccount")
|
||||
if err3 != nil {
|
||||
errStr.WriteString(err3.Error())
|
||||
errStr.WriteString("\n")
|
||||
qp.P.LogVerboseMessage("%v\n", err3)
|
||||
qp.P.LogVerboseMessage("Preflight serviceaccount check: FAILED\n")
|
||||
}
|
||||
fmt.Printf("Completed preflight serviceaccount check\n\n")
|
||||
qp.P.LogVerboseMessage("Completed preflight serviceaccount check\n\n")
|
||||
|
||||
fmt.Println("Preflight RB check: PASSED")
|
||||
fmt.Println("Completed preflight CreateRB check")
|
||||
if err1 != nil || err2 != nil || err3 != nil {
|
||||
qp.P.LogVerboseMessage("Preflight authcheck: FAILED\n")
|
||||
qp.P.LogVerboseMessage("Completed preflight authcheck\n")
|
||||
return errors.New(errStr.String())
|
||||
}
|
||||
qp.P.LogVerboseMessage("Preflight authcheck: PASSED\n")
|
||||
qp.P.LogVerboseMessage("Completed preflight authcheck\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
)
|
||||
|
||||
func (qp *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigContents []byte) error {
|
||||
|
||||
qp.P.LogVerboseMessage("Preflight kubernetes version check: \n")
|
||||
qp.P.LogVerboseMessage("----------------------------------- \n")
|
||||
var currentVersion *semver.Version
|
||||
|
||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||
@@ -22,35 +24,29 @@ func (qp *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigConten
|
||||
return err
|
||||
}); err != nil {
|
||||
err = fmt.Errorf("Unable to get server version: %v\n", err)
|
||||
//fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Kubernetes API Server version: %s\n", serverVersion.String())
|
||||
qp.P.LogVerboseMessage("Kubernetes API Server version: %s\n", serverVersion.String())
|
||||
|
||||
// Compare K8s version on the cluster with minimum supported k8s version
|
||||
currentVersion, err = semver.NewVersion(serverVersion.String())
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Unable to convert server version into semver version: %v\n", err)
|
||||
//fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
//fmt.Printf("Current K8s Version: %v\n", currentVersion)
|
||||
api.LogDebugMessage("Current Kubernetes Version: %v\n", currentVersion)
|
||||
|
||||
minK8sVersionSemver, err := semver.NewVersion(qp.GetPreflightConfigObj().GetMinK8sVersion())
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Unable to convert minimum Kubernetes version into semver version:%v\n", err)
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if currentVersion.GreaterThan(minK8sVersionSemver) {
|
||||
//fmt.Printf("\n\nCurrent %s Component version: %s is less than minimum required version:%s\n", component, currentComponentVersion, componentVersionFromDependenciesYaml)
|
||||
fmt.Printf("Current %s is greater than minimum required version:%s\n", currentVersion, minK8sVersionSemver)
|
||||
fmt.Println("Preflight minimum kubernetes version check: PASSED")
|
||||
qp.P.LogVerboseMessage("Current Kubernetes API Server version %s is greater than or equal to minimum required version: %s\n", currentVersion, minK8sVersionSemver)
|
||||
} else {
|
||||
fmt.Printf("Current %s is less than minimum required version:%s\n", currentVersion, minK8sVersionSemver)
|
||||
fmt.Println("Preflight minimum kubernetes version check: FAILED")
|
||||
err = fmt.Errorf("Current Kubernetes API Server version %s is less than minimum required version: %s", currentVersion, minK8sVersionSemver)
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Completed Preflight kubernetes minimum version check\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -194,8 +194,10 @@ func getImageList(yamlContent []byte) ([]string, error) {
|
||||
})
|
||||
}
|
||||
var sortedImageList []string
|
||||
for image, _ := range imageMap {
|
||||
for image, v := range imageMap {
|
||||
sortedImageList = append(sortedImageList, image)
|
||||
// a warning "simplify range expression" if written like this 'for image _ :=range imageMap'
|
||||
_ = v
|
||||
}
|
||||
sort.Strings(sortedImageList)
|
||||
return sortedImageList, nil
|
||||
|
||||
@@ -2,13 +2,13 @@ package qliksense
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func Test_About_getImageList(t *testing.T) {
|
||||
@@ -230,15 +230,24 @@ spec:
|
||||
}
|
||||
}
|
||||
|
||||
func Test_foo(t *testing.T) {
|
||||
configDir := "C:\\Users\\abulynko\\AppData\\Local\\Temp\\03994898/repo"
|
||||
fmt.Printf("--AB: 1: %v\n", path.Dir(configDir))
|
||||
|
||||
sub := path.Dir(configDir)
|
||||
fmt.Printf("--AB: 2: %v\n", path.Dir(sub))
|
||||
}
|
||||
|
||||
func Test_About_getConfigDirectory(t *testing.T) {
|
||||
verifyAsdBranch := func(configDir string) (ok bool, reason string) {
|
||||
tmpDir := os.TempDir()
|
||||
|
||||
if (path.Clean(path.Dir(path.Dir(configDir))) != path.Clean(tmpDir)) || (path.Base(configDir) != "repo") {
|
||||
configParentDir := filepath.Dir(configDir)
|
||||
if (filepath.Clean(filepath.Dir(configParentDir)) != filepath.Clean(tmpDir)) || (filepath.Base(configDir) != "repo") {
|
||||
return false, fmt.Sprintf("expected config directory path: %v to be under: %v and terminate with repo", configDir, tmpDir)
|
||||
}
|
||||
|
||||
if info, err := os.Stat(path.Join(configDir, "asdczxc")); err != nil || !info.Mode().IsRegular() {
|
||||
if info, err := os.Stat(filepath.Join(configDir, "asdczxc")); err != nil || !info.Mode().IsRegular() {
|
||||
return false, fmt.Sprintf(`expected to find file: "asdczxc" under directory: %v`, configDir)
|
||||
}
|
||||
return true, ""
|
||||
@@ -247,15 +256,16 @@ func Test_About_getConfigDirectory(t *testing.T) {
|
||||
verifyMasterBranch := func(configDir string) (ok bool, reason string) {
|
||||
tmpDir := os.TempDir()
|
||||
|
||||
if (path.Clean(path.Dir(path.Dir(configDir))) != path.Clean(tmpDir)) || (path.Base(configDir) != "repo") {
|
||||
configParentDir := filepath.Dir(configDir)
|
||||
if (filepath.Clean(filepath.Dir(configParentDir)) != filepath.Clean(tmpDir)) || (filepath.Base(configDir) != "repo") {
|
||||
return false, fmt.Sprintf("expected config directory path: %v to be under: %v and terminate with repo", configDir, tmpDir)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path.Join(configDir, "asdczxc")); err == nil || !os.IsNotExist(err) {
|
||||
if _, err := os.Stat(filepath.Join(configDir, "asdczxc")); err == nil || !os.IsNotExist(err) {
|
||||
return false, fmt.Sprintf(`expected to NOT find file: "asdczxc"" under directory: %v`, configDir)
|
||||
}
|
||||
|
||||
if info, err := os.Stat(path.Join(configDir, "sad")); err != nil || !info.Mode().IsRegular() {
|
||||
if info, err := os.Stat(filepath.Join(configDir, "sad")); err != nil || !info.Mode().IsRegular() {
|
||||
return false, fmt.Sprintf(`expected to find file: "sad"" under directory: %v`, configDir)
|
||||
}
|
||||
return true, ""
|
||||
@@ -305,7 +315,7 @@ func Test_About_getConfigDirectory(t *testing.T) {
|
||||
cleanup: func(_ *Qliksense, configDir string) error {
|
||||
if currentDirectory, err := os.Getwd(); err != nil {
|
||||
return err
|
||||
} else if err := os.RemoveAll(path.Join(currentDirectory, "manifests")); err != nil {
|
||||
} else if err := os.RemoveAll(filepath.Join(currentDirectory, "manifests")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -320,7 +330,7 @@ func Test_About_getConfigDirectory(t *testing.T) {
|
||||
}
|
||||
|
||||
profileEntered = "foo"
|
||||
defaultProfilePath := path.Join(currentDirectory, "manifests", profileEntered)
|
||||
defaultProfilePath := filepath.Join(currentDirectory, "manifests", profileEntered)
|
||||
err = os.MkdirAll(defaultProfilePath, os.ModePerm)
|
||||
if err != nil {
|
||||
t.Fatalf("error making path: %v, err: %v\n", defaultProfilePath, err)
|
||||
@@ -350,7 +360,7 @@ func Test_About_getConfigDirectory(t *testing.T) {
|
||||
cleanup: func(_ *Qliksense, configDir string) error {
|
||||
if currentDirectory, err := os.Getwd(); err != nil {
|
||||
return err
|
||||
} else if err := os.RemoveAll(path.Join(currentDirectory, "manifests")); err != nil {
|
||||
} else if err := os.RemoveAll(filepath.Join(currentDirectory, "manifests")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -380,8 +390,8 @@ func Test_About_getConfigDirectory(t *testing.T) {
|
||||
cleanup: func(_ *Qliksense, configDir string) error {
|
||||
tmpDir := os.TempDir()
|
||||
|
||||
if path.Clean(path.Dir(path.Dir(configDir))) == path.Clean(tmpDir) && path.Base(configDir) == "repo" {
|
||||
tmpTmpDir := path.Dir(configDir)
|
||||
tmpTmpDir := filepath.Dir(configDir)
|
||||
if filepath.Clean(filepath.Dir(tmpTmpDir)) == filepath.Clean(tmpDir) && filepath.Base(configDir) == "repo" {
|
||||
if err := os.RemoveAll(tmpTmpDir); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -413,8 +423,8 @@ func Test_About_getConfigDirectory(t *testing.T) {
|
||||
cleanup: func(_ *Qliksense, configDir string) error {
|
||||
tmpDir := os.TempDir()
|
||||
|
||||
if path.Clean(path.Dir(path.Dir(configDir))) == path.Clean(tmpDir) && path.Base(configDir) == "repo" {
|
||||
tmpTmpDir := path.Dir(configDir)
|
||||
tmpTmpDir := filepath.Dir(configDir)
|
||||
if filepath.Clean(filepath.Dir(tmpTmpDir)) == filepath.Clean(tmpDir) && filepath.Base(configDir) == "repo" {
|
||||
if err := os.RemoveAll(tmpTmpDir); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -456,8 +466,9 @@ func Test_About_getConfigDirectory(t *testing.T) {
|
||||
},
|
||||
cleanup: func(q *Qliksense, configDir string) error {
|
||||
tmpDir := os.TempDir()
|
||||
if path.Clean(path.Dir(path.Dir(configDir))) == path.Clean(tmpDir) && path.Base(configDir) == "repo" {
|
||||
tmpTmpDir := path.Dir(configDir)
|
||||
|
||||
tmpTmpDir := filepath.Dir(configDir)
|
||||
if filepath.Clean(filepath.Dir(tmpTmpDir)) == filepath.Clean(tmpDir) && filepath.Base(configDir) == "repo" {
|
||||
if err := os.RemoveAll(tmpTmpDir); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -502,8 +513,9 @@ func Test_About_getConfigDirectory(t *testing.T) {
|
||||
},
|
||||
cleanup: func(q *Qliksense, configDir string) error {
|
||||
tmpDir := os.TempDir()
|
||||
if path.Clean(path.Dir(path.Dir(configDir))) == path.Clean(tmpDir) && path.Base(configDir) == "repo" {
|
||||
tmpTmpDir := path.Dir(configDir)
|
||||
|
||||
tmpTmpDir := filepath.Dir(configDir)
|
||||
if filepath.Clean(filepath.Dir(tmpTmpDir)) == filepath.Clean(tmpDir) && filepath.Base(configDir) == "repo" {
|
||||
if err := os.RemoveAll(tmpTmpDir); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -525,12 +537,16 @@ func Test_About_getConfigDirectory(t *testing.T) {
|
||||
if err := q.SetUpQliksenseDefaultContext(); err != nil {
|
||||
t.Fatalf("error setting up default context in the tmp dir: %v\n", err)
|
||||
return nil, "", "", ""
|
||||
} else if err := q.FetchQK8s("master"); err != nil {
|
||||
t.Fatalf("error fetching master config to the tmp dir: %v\n", err)
|
||||
} else if qConfig, err := qapi.NewQConfigE(q.QliksenseHome); err != nil {
|
||||
t.Fatalf("cannot initiallize qConfig: %v\n", err)
|
||||
return nil, "", "", ""
|
||||
} else {
|
||||
return q, "no-git-clone-for-you", "", ""
|
||||
} else if !qConfig.IsRepoExistForCurrent("master") {
|
||||
if err := q.FetchQK8s("master"); err != nil {
|
||||
t.Fatalf("error fetching master config to the tmp dir: %v\n", err)
|
||||
return nil, "", "", ""
|
||||
}
|
||||
}
|
||||
return q, "no-git-clone-for-you", "", ""
|
||||
}
|
||||
},
|
||||
verify: func(q *Qliksense, configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func (q *Qliksense) ApplyCRFromReader(r io.Reader, opts *InstallCommandOptions, keepPatchFiles, overwriteExistingContext bool) error {
|
||||
func (q *Qliksense) ApplyCRFromReader(r io.Reader, opts *InstallCommandOptions, keepPatchFiles, overwriteExistingContext, pull, push bool) error {
|
||||
if err := q.LoadCr(r, overwriteExistingContext); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -15,16 +16,33 @@ func (q *Qliksense) ApplyCRFromReader(r io.Reader, opts *InstallCommandOptions,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
version := cr.GetLabelFromCr("version")
|
||||
|
||||
if pull {
|
||||
fmt.Println("Pulling images...")
|
||||
if err := q.PullImages(version, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if push {
|
||||
fmt.Println("Pushing images...")
|
||||
if err := q.PushImagesForCurrentCR(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if IsQliksenseInstalled(cr.GetName()) {
|
||||
// it is needed in case want to upgrade from one version to another
|
||||
if cr.Spec.ManifestsRoot == "" && cr.Spec.Git == nil {
|
||||
if err := q.FetchQK8s(cr.GetLabelFromCr("version")); err != nil {
|
||||
return err
|
||||
if !qConfig.IsRepoExistForCurrent(version) {
|
||||
if err := q.FetchQK8s(version); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return q.UpgradeQK8s(keepPatchFiles)
|
||||
}
|
||||
return q.InstallQK8s(cr.GetLabelFromCr("version"), opts, keepPatchFiles)
|
||||
return q.InstallQK8s(version, opts, keepPatchFiles)
|
||||
}
|
||||
|
||||
func IsQliksenseInstalled(crName string) bool {
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Q_INIT_CRD_PATH = "manifests/base/manifests/qliksense-init"
|
||||
Q_INIT_CRD_PATH = "manifests/base/crds"
|
||||
agreementTempalte = `
|
||||
Please read the agreement at https://www.qlik.com/us/legal/license-terms
|
||||
Accept the end user license agreement by providing acceptEULA=yes
|
||||
@@ -146,18 +146,22 @@ func (q *Qliksense) getCurrentCrDependentResourceAsString() (string, error) {
|
||||
var crString strings.Builder
|
||||
|
||||
for svcName, v := range qcr.Spec.Secrets {
|
||||
hasFile := false
|
||||
for _, item := range v {
|
||||
if item.ValueFrom != nil && item.ValueFrom.SecretKeyRef != nil {
|
||||
secretFilePath := filepath.Join(q.QliksenseHome, QliksenseContextsDir, qcr.GetName(), QliksenseSecretsDir, svcName+".yaml")
|
||||
|
||||
if api.FileExists(secretFilePath) {
|
||||
secretFile, err := ioutil.ReadFile(secretFilePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
crString.WriteString("\n---\n")
|
||||
crString.Write(secretFile)
|
||||
hasFile = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasFile {
|
||||
secretFilePath := filepath.Join(q.QliksenseHome, QliksenseContextsDir, qcr.GetName(), QliksenseSecretsDir, svcName+".yaml")
|
||||
if api.FileExists(secretFilePath) {
|
||||
secretFile, err := ioutil.ReadFile(secretFilePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
crString.WriteString("\n---\n")
|
||||
crString.Write(secretFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
24
pkg/qliksense/confirmation.go
Normal file
24
pkg/qliksense/confirmation.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func AskForConfirmation(s string) bool {
|
||||
for {
|
||||
fmt.Printf("%s [y/n]: ", s)
|
||||
var response string
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if strings.EqualFold(response, "y") || strings.EqualFold(response, "yes") {
|
||||
return true
|
||||
} else if strings.EqualFold(response, "n") || strings.EqualFold(response, "no") {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/qlik-oss/k-apis/pkg/config"
|
||||
"github.com/robfig/cron/v3"
|
||||
@@ -33,12 +34,27 @@ const (
|
||||
MaxContextNameLength = 17
|
||||
QliksenseSecretsDir = "secrets"
|
||||
|
||||
imageRegistryConfigKey = "imageRegistry"
|
||||
pullSecretName = "artifactory-docker-secret"
|
||||
imageRegistryConfigKey = "imageRegistry"
|
||||
pullSecretName = "artifactory-docker-secret"
|
||||
qliksenseOperatorImageRepo = "qlik-docker-oss.bintray.io"
|
||||
qliksenseOperatorImageName = "qliksense-operator"
|
||||
)
|
||||
|
||||
func (q *Qliksense) SetSecretsFromReader(arg string, reader io.Reader, createSecret, base64Encoded bool) error {
|
||||
//take only name from the arguments, value should be from reader
|
||||
argName := strings.SplitN(arg, "=", 1)
|
||||
if len(argName) != 1 {
|
||||
return errors.New("can only have one argument from pipe")
|
||||
}
|
||||
valueBytes, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return q.SetSecrets([]string{argName[0] + "=" + string(valueBytes)}, createSecret, base64Encoded)
|
||||
}
|
||||
|
||||
// SetSecrets - set-secrets <key>=<value> commands
|
||||
func (q *Qliksense) SetSecrets(args []string, isSecretSet bool) error {
|
||||
func (q *Qliksense) SetSecrets(args []string, isSecretSet bool, base64Encoded bool) error {
|
||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||
qliksenseCR, err := qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
@@ -47,17 +63,17 @@ func (q *Qliksense) SetSecrets(args []string, isSecretSet bool) error {
|
||||
|
||||
// Metadata name in qliksense CR is the name of the current context
|
||||
api.LogDebugMessage("Current context: %s", qliksenseCR.GetName())
|
||||
rsaPublicKey, _, err := qConfig.GetCurrentContextEncryptionKeyPair()
|
||||
encryptionKey, err := qConfig.GetEncryptionKeyForCurrent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resultArgs, err := api.ProcessConfigArgs(args)
|
||||
resultArgs, err := api.ProcessConfigArgs(args, base64Encoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ra := range resultArgs {
|
||||
api.LogDebugMessage("value args to be encrypted: %s", ra.Value)
|
||||
if err := q.processSecret(ra, rsaPublicKey, qliksenseCR, isSecretSet); err != nil {
|
||||
if err := q.processSecret(ra, encryptionKey, qliksenseCR, isSecretSet); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -65,14 +81,11 @@ func (q *Qliksense) SetSecrets(args []string, isSecretSet bool) error {
|
||||
return qConfig.WriteCR(qliksenseCR)
|
||||
}
|
||||
|
||||
func (q *Qliksense) processSecret(ra *api.ServiceKeyValue, rsaPublicKey *rsa.PublicKey, qliksenseCR *api.QliksenseCR, isSecretSet bool) error {
|
||||
// encrypt value with RSA key pair
|
||||
valueBytes := []byte(ra.Value)
|
||||
cipherText, e2 := api.Encrypt(valueBytes, rsaPublicKey)
|
||||
func (q *Qliksense) processSecret(ra *api.ServiceKeyValue, encryptionKey string, qliksenseCR *api.QliksenseCR, isSecretSet bool) error {
|
||||
cipherText, e2 := api.EncryptData([]byte(ra.Value), encryptionKey)
|
||||
if e2 != nil {
|
||||
return e2
|
||||
}
|
||||
base64EncodedSecret := b64.StdEncoding.EncodeToString(cipherText)
|
||||
secretName := ""
|
||||
if isSecretSet {
|
||||
secretFolder := qliksenseCR.GetK8sSecretsFolder(q.QliksenseHome)
|
||||
@@ -104,7 +117,8 @@ func (q *Qliksense) processSecret(ra *api.ServiceKeyValue, rsaPublicKey *rsa.Pub
|
||||
if k8sSecret.Data == nil {
|
||||
k8sSecret.Data = map[string][]byte{}
|
||||
}
|
||||
k8sSecret.Data[ra.Key] = []byte(base64EncodedSecret)
|
||||
// v1.Secret does enconding, so no need to encode again
|
||||
k8sSecret.Data[ra.Key] = []byte(cipherText)
|
||||
|
||||
// Write secret to file
|
||||
k8sSecretBytes, err := api.K8sSecretToYaml(k8sSecret)
|
||||
@@ -117,18 +131,28 @@ func (q *Qliksense) processSecret(ra *api.ServiceKeyValue, rsaPublicKey *rsa.Pub
|
||||
return err
|
||||
}
|
||||
api.LogDebugMessage("Created a Kubernetes secret")
|
||||
|
||||
// Prepare args to update CR in the next step
|
||||
base64EncodedSecret = ""
|
||||
}
|
||||
|
||||
base64EncodedSecret := b64.StdEncoding.EncodeToString([]byte(cipherText))
|
||||
// write into CR the keyref of the secret
|
||||
qliksenseCR.Spec.AddToSecrets(ra.SvcName, ra.Key, base64EncodedSecret, secretName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) SetConfigFromReader(arg string, reader io.Reader, base64Encoded bool) error {
|
||||
//take only name from the arguments, value should be from reader
|
||||
argName := strings.SplitN(arg, "=", 1)
|
||||
if len(argName) != 1 {
|
||||
return errors.New("can only have one argument from pipe")
|
||||
}
|
||||
valueBytes, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return q.SetConfigs([]string{argName[0] + "=" + string(valueBytes)}, base64Encoded)
|
||||
}
|
||||
|
||||
// SetConfigs - set-configs <key>=<value> commands
|
||||
func (q *Qliksense) SetConfigs(args []string) error {
|
||||
func (q *Qliksense) SetConfigs(args []string, base64Encoded bool) error {
|
||||
// retieve current context from config.yaml
|
||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||
qliksenseCR, err := qConfig.GetCurrentCR()
|
||||
@@ -136,7 +160,7 @@ func (q *Qliksense) SetConfigs(args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
resultArgs, err := api.ProcessConfigArgs(args)
|
||||
resultArgs, err := api.ProcessConfigArgs(args, base64Encoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -216,43 +240,130 @@ func (q *Qliksense) SetOtherConfigs(args []string) error {
|
||||
}
|
||||
|
||||
for _, arg := range args {
|
||||
argsString := strings.Split(arg, "=")
|
||||
key := strings.ToLower(argsString[0])
|
||||
value := argsString[1]
|
||||
// check if key is for git or gitops (sub objects)
|
||||
keySplit := strings.Split(key, ".")
|
||||
key = keySplit[0]
|
||||
keySub := ""
|
||||
|
||||
if len(keySplit) == 2 {
|
||||
keySub = strings.ToLower(keySplit[1])
|
||||
}
|
||||
|
||||
valid := true
|
||||
valid, qliksenseCR = validateCR(key, keySub, value, qliksenseCR)
|
||||
field := caseInsenstiveFieldByName(reflect.Indirect(reflect.ValueOf(qliksenseCR.Spec)), key)
|
||||
if !valid {
|
||||
err := fmt.Errorf("Please enter one of: profile, storageClassName,rotateKeys, manifestRoot, git.repository or gitops arguments to configure the current context")
|
||||
return err
|
||||
} else if strings.EqualFold("", keySub) {
|
||||
// set spec for everything excluding git and gitops
|
||||
if field.CanSet() {
|
||||
field.SetString(value)
|
||||
if strings.HasPrefix(arg, "fetchSource.") {
|
||||
if err := q.processSetFetchSource(arg, qliksenseCR); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if strings.HasPrefix(arg, "git.") {
|
||||
if err := q.processSetGit(arg, qliksenseCR); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if strings.HasPrefix(arg, "gitOps.") {
|
||||
if err := q.processSetGitOps(arg, qliksenseCR); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// set spec for git or gitops
|
||||
subField := caseInsenstiveFieldByName(reflect.Indirect(field), keySub)
|
||||
if subField.CanSet() {
|
||||
subField.SetString(value)
|
||||
if err := processSetSingleArg(arg, qliksenseCR); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(chalk.Green.Color("Successfully added to Custom Resource Spec"))
|
||||
}
|
||||
|
||||
// write modified content into context.yaml
|
||||
return qConfig.WriteCR(qliksenseCR)
|
||||
}
|
||||
|
||||
func processSetSingleArg(arg string, cr *api.QliksenseCR) error {
|
||||
nv := strings.Split(arg, "=")
|
||||
switch nv[0] {
|
||||
case "manifestsRoot":
|
||||
cr.Spec.ManifestsRoot = nv[1]
|
||||
case "profile":
|
||||
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 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{}
|
||||
}
|
||||
switch subs[1] {
|
||||
case "repository":
|
||||
cr.Spec.FetchSource.Repository = args[1]
|
||||
case "accessToken":
|
||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||
key, err := qConfig.GetEncryptionKeyFor(cr.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cr.SetFetchAccessToken(args[1], key)
|
||||
case "secretName":
|
||||
cr.Spec.FetchSource.SecretName = args[1]
|
||||
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]
|
||||
default:
|
||||
return errors.New(arg + " does not match any cr spec")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) processSetGitOps(arg string, cr *api.QliksenseCR) error {
|
||||
args := strings.Split(arg, "=")
|
||||
subs := strings.Split(args[0], ".")
|
||||
if cr.Spec.Git == nil {
|
||||
cr.Spec.GitOps = &config.GitOps{}
|
||||
}
|
||||
switch subs[1] {
|
||||
case "enabled":
|
||||
if args[1] != "yes" && args[1] != "no" {
|
||||
return errors.New("Please use yes or no for key enabled")
|
||||
}
|
||||
cr.Spec.GitOps.Enabled = args[1]
|
||||
case "schedule":
|
||||
if _, err := cron.ParseStandard(args[1]); err != nil {
|
||||
return errors.New("Please enter string with standard cron scheduling syntax ")
|
||||
}
|
||||
cr.Spec.GitOps.Schedule = args[1]
|
||||
case "watchBranch":
|
||||
cr.Spec.GitOps.WatchBranch = args[1]
|
||||
case "image":
|
||||
cr.Spec.GitOps.Image = args[1]
|
||||
default:
|
||||
return errors.New(arg + " does not match any cr spec")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetContextConfig - set the context for qliksense kubernetes resources to live in
|
||||
func (q *Qliksense) SetContextConfig(args []string) error {
|
||||
if len(args) == 1 {
|
||||
@@ -292,7 +403,7 @@ func (q *Qliksense) ListContextConfigs() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) DeleteContextConfig(args []string) error {
|
||||
func (q *Qliksense) DeleteContextConfig(args []string, flag bool) error {
|
||||
if len(args) == 1 {
|
||||
qliksenseConfigFile := filepath.Join(q.QliksenseHome, QliksenseConfigFile)
|
||||
var qliksenseConfig api.QliksenseConfig
|
||||
@@ -334,9 +445,17 @@ func (q *Qliksense) DeleteContextConfig(args []string) error {
|
||||
}
|
||||
newLength := len(qliksenseConfig.Spec.Contexts)
|
||||
if currentLength != newLength {
|
||||
api.WriteToFile(&qliksenseConfig, qliksenseConfigFile)
|
||||
fmt.Fprintln(out, chalk.Yellow.Color(chalk.Underline.TextStyle("Warning: Active resources may still be running in-cluster")))
|
||||
fmt.Fprintln(out, chalk.Green.Color("Successfully deleted context: "), chalk.Bold.TextStyle(args[0]))
|
||||
ans := flag
|
||||
if ans == false {
|
||||
ans = AskForConfirmation("Are You Sure? ")
|
||||
}
|
||||
if ans == true {
|
||||
api.WriteToFile(&qliksenseConfig, qliksenseConfigFile)
|
||||
fmt.Fprintln(out, chalk.Yellow.Color(chalk.Underline.TextStyle("Warning: Active resources may still be running in-cluster")))
|
||||
fmt.Fprintln(out, chalk.Green.Color("Successfully deleted context: "), chalk.Bold.TextStyle(args[0]))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
err := fmt.Errorf(chalk.Red.Color("Context not found"))
|
||||
return err
|
||||
@@ -401,7 +520,7 @@ func (q *Qliksense) SetUpQliksenseContext(contextName string) error {
|
||||
}
|
||||
|
||||
// set the encrypted default mongo
|
||||
return q.SetSecrets([]string{`qliksense.mongoDbUri="mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"`}, false)
|
||||
return q.SetSecrets([]string{`qliksense.mongoDbUri="mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"`}, false, false)
|
||||
}
|
||||
|
||||
func validateInput(input string) (string, error) {
|
||||
@@ -422,7 +541,8 @@ func validateInput(input string) (string, error) {
|
||||
return input, err
|
||||
}
|
||||
|
||||
// PrepareK8sSecret decodes and decrypts the secret value in the secret.yaml file and returns a B64encoded string
|
||||
// PrepareK8sSecret targetFile contains base64 encoded value of encrypted value.
|
||||
// this method decodes and decrypts the secret value in the secret.yaml file and returns a B64encoded string
|
||||
func (q *Qliksense) PrepareK8sSecret(targetFile string) (string, error) {
|
||||
// check if targetFile exists
|
||||
if !api.FileExists(targetFile) {
|
||||
@@ -431,7 +551,7 @@ func (q *Qliksense) PrepareK8sSecret(targetFile string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||
_, rsaPrivateKey, err := qConfig.GetCurrentContextEncryptionKeyPair()
|
||||
encryptionKey, err := qConfig.GetEncryptionKeyForCurrent()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -449,17 +569,13 @@ func (q *Qliksense) PrepareK8sSecret(targetFile string) (string, error) {
|
||||
dataMap := k8sSecret1.Data
|
||||
var resultMap = make(map[string][]byte)
|
||||
for k, v := range dataMap {
|
||||
ba, err := b64.StdEncoding.DecodeString(string(v))
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Not able to decode message: %v", err)
|
||||
return "", err
|
||||
}
|
||||
decryptedString, err := api.Decrypt(ba, rsaPrivateKey)
|
||||
//k8s secrets has already base64 decoed value
|
||||
decryptedString, err := api.DecryptData(v, encryptionKey)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Not able to decrypt message: %v", err)
|
||||
return "", err
|
||||
}
|
||||
resultMap[k] = decryptedString
|
||||
resultMap[k] = []byte(decryptedString)
|
||||
}
|
||||
|
||||
// putting the above map back into the k8sSecret struct
|
||||
|
||||
@@ -35,119 +35,35 @@ metadata:
|
||||
name: testctx-qliksense-senseinstaller
|
||||
type: Opaque
|
||||
`
|
||||
var encText = "SFpVZ2t5SGsrN2lLQjlTYm9rbFUxSDFRcmVYdUxhTW9MUHlQOGtGditxMEcwZTlIZDl1dVRrV0tEYm5qUURSWFp3dStuNklueGk3anI2c1djSVdsbWlKTHdWQUJwdUg0a1NXd3llMUlMa2oxK3FRSFlMM2dQUExvN1pBYkVDeDROMUVvam12M0t0NmQwbkdhSXlWWEpmWWJUVVFDM1Y4L0ZTVXBVN0JUb0l4OVZWdmlPam5HTHk4RlF2a3RUaHJxWTUvZEh2N3pVUmhiOTc2Q2YwbEovZ3I2L2NwRk9RMUFXVXdodVhrTG9lYjVzNFdtTEZzNldqT3k0bWlKM1J6VllLaWVUSFJ2SE85eDB6dUthanRwSGEzWEZkaE5QNnpySVJJNTRFalUyblVYYUNlYXVnWnZEOUxjdWluOFhFcjExbkFINURCUDAycXhoZk5BejVoMlV2eFNWVmR0aW1QTDBhMVBJTUxGQTgyWUkrQkFOQkhkSUNnZGU5SkxIRFBoTzR6c0llaE1LRmhVQkNoOUhQa3kyRnhTeDJ3YWp3M1UycEsvcFJVZUxDazRUbkhmL25LN3h5ekdpV3dSUFFFZHdsWE5JbUhjVlVPV3gvNWh4WlJCUTZtb3pGYk1HbXR1Mkh5Z3RVV2gzNFYzd1BhS01TNFRsa0hyODFjRjVCWVpxenBFK1pKWnVyLy8zbzJsU0tFMjMxTG1pcGk1K0FqbXZvUVcyWHBocjFNVWJQY1pXUkJFRkkyQXBCM0FhQXFPa0k1MkRqNG43Mko5bCtaMzdydTk1aHk5K1lzY0FxMjZVbExYRlc0S3RUUkRLSjlMNnVmdlIrUUNudER3em5UTFRHUnEwZU5COWt6S0Q4MFlUdXozeHNXK3cxdjlHbDJaMnBZMTZWTCtEV1k9"
|
||||
var decText = "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"
|
||||
|
||||
func setupTargetFileAndPrivateKey() ([]byte, []byte, string, error) {
|
||||
targetFileString := fmt.Sprintf(targetFileStringTemplate, encText)
|
||||
privKeyBytes := []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJJwIBAAKCAgEApFf3qCQhAr2QLRRZdhLyB8exLjrQiXLr8hwDe0xHSLJX3w7v
|
||||
5z4ujJWrHUulQ2/hvS8uffxMVrp2YqeA4sjy/ku1KqZVQv/WNTdL2v9Z1ewbRnBj
|
||||
DQvmkWDKZ+cP8VPdHGzQ4iM2z6BZ4RQTkdQMKqsVwUsLO9amI2TOny/M696eFRW0
|
||||
pk4+W3QZZRawT0HqJPvKKXKqoO2+62W54rOV8glJi29Do06e4S6CZUl1hBUy0VlL
|
||||
trLlRSOHTois0dF2a9f7+GGgU11MHO6w1k1NesSlfZ0vnrrkW8WFLqewk+Jj+w1x
|
||||
eQnYHOeHjx6zi9f9DC96eSylSB3iJ71NmXcMc0IEZ2LiQIqTL83BLOgMBCsK3FSl
|
||||
GMakQUR2GJ+M0I/selYkRMhid6eOmhlsTNMPbpcTHxZ+ReIzS+5B1X0FZ7RIL+jS
|
||||
L9mFcxmD3dxurrrt/DkLpXcuWdi1s1bpVn3jIQhU0+bgA6hT0k8Kj2f3Q9QnvkHS
|
||||
Eff+XyLvLwQeSsSAcnM+1I7fNSPEo2cq0au6ZtjHcirXmMminAQ6cKW1XrEvJBef
|
||||
HHibtjJjIM4bHH7MKRA5H3km/J4CCwI1VogSTcE05Z6ypAFU2TCrnec9c9VXkRDP
|
||||
94h0GuoG8sdhmQudqvghr/8T7CV+sGRQbdeqrXwanfnGPrjcMVIO/dSOxOUCAwEA
|
||||
AQKCAgA5b2TmJnpC8u0IVCxPz582iNurRHLNFpTPMGsnFCl1hp6fHiFJt7mc+FGt
|
||||
E1rWjqtd6rdc4Gfth40IPXIV0BTcOqk+FpOFrtO2FXU1PDixQqrlmzGCxb324NTc
|
||||
KyyvMpf77yuxXI0zUt8WgmW0eV8nKlOYEhoC96lohTqQ96uuY0bsJ4HS/VVdsN2P
|
||||
Lra/fFHQSw8EHUb0pyIqMoscZ5bn18cUK/Z/hGKSYCbCL0Iavy3bbFHBsBPgbeJD
|
||||
2BBN4953Iiy1Sak2eUy4b9LtkmaZmVAc7mpOFxLn38gD3icgB+bZPoGBw6b7sw71
|
||||
Pc2R+hI9x/oNj0TUR11adhZApBJ9RhBbnSCUt8OUt9U5prNj+9qs8cHJGywtz1da
|
||||
ZT1M6mn2MFSsaOyOlJPzGUzSf4AhI7HiouDpLHtHDqLmc8Vv4rZUqrcFw6kZTCY5
|
||||
564yE8hh/UimOgQr7467/hADHZ0kBsupFEDWRqQ3qTIikHmGhTYZehDrSGL/3BMG
|
||||
rvsFdv0krUHyW4FfHqPN09jfP3LTqd5vVbzRhxcGsoGmP/1kXIDtO8Cp1s/K6Mse
|
||||
tInRCRla8ttZ3CZZ+Vf8HLi/n8XSRfbnMGYi7lVVxnp6kNsTEBgosbdU/r1zbMRJ
|
||||
8mMMHygyugaRLHmOD/8fkWLfyR88cxPH8u9NufTfvJgiFpZboQKCAQEA0uSU6IGZ
|
||||
pXIVZdmDWt4mxpS5T2UYarw3V9z/Isd3kkUU5YrC2XrvPRwmx5Jh9GXl9WENYJAR
|
||||
wH7PaJT0HpBwpxJa1RqHHDSka7DnDcy44oRXyM7e2AmcW8QvcDty/0HPo4oZq4IT
|
||||
m/+ot1R+bIpmJOweGRhVauzxJEUlQyt+kiH/ad8GiOS6LwqFPq42alnUxPQ106wF
|
||||
EZZ2WQdzkyV6tF9aMG18AT1fJsGwNjCLRxJ52t/aEUP5mYwlL2UTT5Acn8KbtrTO
|
||||
fFLAxGuB9LDdT1tGgIpzsXmxAaaeuPvSK4TDFdQyLAUdQJdz0GD9j9ciMPQH3UPe
|
||||
Vjt6qtpfY6QK2wKCAQEAx36Vys5BlQI0TG6qORI0fiOYpLG1GqmdbCNRgBUsMj5T
|
||||
LFe7uSd4qnDvGmns4MdkSSOlpF17bQiWhWKbjKRQpT0U/46zcIT4pWyajXJe+i9H
|
||||
M/DpSRkMq2kGkx6KX2u9L66QBzcxJjtS17amdSpDAfsrvJgOWkxxInzw9n1u6aTe
|
||||
ZjRDXdVX0KjPebEPOaoJToxne21Od3t+47TnDsQPsO1dvvrXX76IfH8cAlD5+0C/
|
||||
b2YvDqWDmh9ICjKShwuDWgi4KjCV5PMHCIxH0FQ2L6mSbwIb9YgGin3wjN3KbWqz
|
||||
dgEu7MeDxEwxZSSg4OstYVLQVgM39G/2ZA4YVJEbPwKCAQAo9FjymhBzb6c2Izp+
|
||||
D/wpvkIKaBCI0cpRlso5P9E5p466UOsr/tKs5GWnhgbdxlgVAebuJKw93KJ8pciO
|
||||
kvA9kbPwBHnOgW6Ytz73kBUrcBX4GixueddSftPTkMfxSB+Bm9UGWHlkZw6lo5P1
|
||||
kh7p9qyVpQMZg7AEoiTtWWn4CQAn2DbVqM17Syi7Fmvc1VsbcG1vkM1fMAAFpAvO
|
||||
vI2Kr6W9F9XoC7oJtb15mI3DnJPrbGNVzQSQzAWAoblRTyQv5kQFBDHBNPTYcCRJ
|
||||
l3sy6P/VAI4dHgvAzVGvjL+w0dRszct8fvXCUGceRWeYYmfyZ8GLN53a0ywsN8Ik
|
||||
gHvXAoIBACee5HEa9bt6bJihgf1DuFk1CKPtB2L8PN+1RAKEMfrolexAoG/tfvGa
|
||||
7GH6l6ks8KX2BnfWeST2h66GHw6Xs8ydjQYUeV7nidqQ70EYbfSSXznZpvt1liaU
|
||||
/VFKx4CcDT7jFIfaVlCZh6KADB9I/XXvRIh4SqF0fSO0XMcXsmeE7watapPAQ2iV
|
||||
nl804yk4tBB9oi/JTcQ9Kr5et2UfW15wRiYf+5ZwaPsQ46cyHfPgsCSXztDB3plF
|
||||
jTE5ShC4IKZJBQqcC6kk+0ifU8P0da6RpxuU96iUE3h9+sB/bCy+/FV7dq5gEbNy
|
||||
znygAbOqAaFKqUXr7bkGY5ELm5lwGFECggEACcyaF9mMqLGghR55ew+cMmdeYdK3
|
||||
meMLi5nrgtbQpVLlz+IV7Vdmrv7lZjeTr4nvU/5miU+p+If14CCFBiSucGq3Kmyp
|
||||
OSM5cNCjDhw8uIDfY2qWCrZ2NSMR3qaAoBAQyQ2ER1IL98TDF/Qui0ZatbPiM4Ns
|
||||
GErhkBZh3MCDSt24yiVKcUB79BxatWB4K7h7y8wqpX4Rj7rpfJMF7wz/I1cgyuCE
|
||||
7XFpRwj7F1B2MmXnvV3KAgAD0EqrJDLeM0vIlDhpOUEaFUkuqmQyeB8qQkWfyXbD
|
||||
jzloS3cNq0MBijB8oixwD2b4dVhBM7z8vQMX6OntN+97luWgO8OIukoYAg==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`)
|
||||
func setupTargetFileAndPrivateKey() (string, string, error) {
|
||||
|
||||
publicKeyBytes := []byte(`-----BEGIN RSA PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApFf3qCQhAr2QLRRZdhLy
|
||||
B8exLjrQiXLr8hwDe0xHSLJX3w7v5z4ujJWrHUulQ2/hvS8uffxMVrp2YqeA4sjy
|
||||
/ku1KqZVQv/WNTdL2v9Z1ewbRnBjDQvmkWDKZ+cP8VPdHGzQ4iM2z6BZ4RQTkdQM
|
||||
KqsVwUsLO9amI2TOny/M696eFRW0pk4+W3QZZRawT0HqJPvKKXKqoO2+62W54rOV
|
||||
8glJi29Do06e4S6CZUl1hBUy0VlLtrLlRSOHTois0dF2a9f7+GGgU11MHO6w1k1N
|
||||
esSlfZ0vnrrkW8WFLqewk+Jj+w1xeQnYHOeHjx6zi9f9DC96eSylSB3iJ71NmXcM
|
||||
c0IEZ2LiQIqTL83BLOgMBCsK3FSlGMakQUR2GJ+M0I/selYkRMhid6eOmhlsTNMP
|
||||
bpcTHxZ+ReIzS+5B1X0FZ7RIL+jSL9mFcxmD3dxurrrt/DkLpXcuWdi1s1bpVn3j
|
||||
IQhU0+bgA6hT0k8Kj2f3Q9QnvkHSEff+XyLvLwQeSsSAcnM+1I7fNSPEo2cq0au6
|
||||
ZtjHcirXmMminAQ6cKW1XrEvJBefHHibtjJjIM4bHH7MKRA5H3km/J4CCwI1VogS
|
||||
TcE05Z6ypAFU2TCrnec9c9VXkRDP94h0GuoG8sdhmQudqvghr/8T7CV+sGRQbdeq
|
||||
rXwanfnGPrjcMVIO/dSOxOUCAwEAAQ==
|
||||
-----END RSA PUBLIC KEY-----
|
||||
`)
|
||||
|
||||
targetFile := filepath.Join(testDir, "targetfile.yaml")
|
||||
// tests/config.yaml exists
|
||||
err := ioutil.WriteFile(targetFile, []byte(targetFileString), 0777)
|
||||
if err != nil {
|
||||
log.Printf("Error while creating file: %v", err)
|
||||
return nil, nil, "", err
|
||||
}
|
||||
|
||||
secretKeyPairDir := filepath.Join(testDir, secrets, contexts, qlikDefaultContext, secrets)
|
||||
if err := os.MkdirAll(secretKeyPairDir, 0777); err != nil {
|
||||
secretKeyLocation := filepath.Join(testDir, secrets, contexts, qlikDefaultContext, secrets)
|
||||
if err := os.MkdirAll(secretKeyLocation, 0777); err != nil {
|
||||
err = fmt.Errorf("Not able to create directories")
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.Setenv("QLIKSENSE_KEY_LOCATION", secretKeyPairDir)
|
||||
os.Setenv("QLIKSENSE_KEY_LOCATION", secretKeyLocation)
|
||||
|
||||
privKeyFile := filepath.Join(secretKeyPairDir, "qliksensePriv")
|
||||
// construct and write priv key file into secretsDir location
|
||||
err = ioutil.WriteFile(privKeyFile, privKeyBytes, 0777)
|
||||
//privKeyFile := filepath.Join(secretKeyLocation, "user_secret_key")
|
||||
key, err := api.LoadSecretKey(secretKeyLocation)
|
||||
if key == "" {
|
||||
key, err = api.GenerateAndStoreSecretKey(secretKeyLocation)
|
||||
}
|
||||
encData, _ := api.EncryptData([]byte(decText), key)
|
||||
encText := b64.StdEncoding.EncodeToString(encData)
|
||||
|
||||
targetFileString := fmt.Sprintf(targetFileStringTemplate, encText)
|
||||
targetFile := filepath.Join(testDir, "targetfile.yaml")
|
||||
// tests/config.yaml exists
|
||||
err = ioutil.WriteFile(targetFile, []byte(targetFileString), 0777)
|
||||
if err != nil {
|
||||
log.Printf("Error while creating file: %v", err)
|
||||
return nil, nil, "", err
|
||||
return "", "", err
|
||||
}
|
||||
pubKeyFile := filepath.Join(secretKeyPairDir, "qliksensePub")
|
||||
api.LogDebugMessage("Test setup - \npub key path: %s\n, priv key path: %s\n", pubKeyFile, privKeyFile)
|
||||
// construct and write pub key file into secretsDir location
|
||||
err = ioutil.WriteFile(pubKeyFile, publicKeyBytes, 0777)
|
||||
if err != nil {
|
||||
log.Printf("Error while creating file: %v", err)
|
||||
return nil, nil, "", err
|
||||
}
|
||||
return publicKeyBytes, privKeyBytes, targetFile, nil
|
||||
}
|
||||
|
||||
func removePrivateKey() {
|
||||
err := os.Remove(filepath.Join(testDir, secrets, contexts, qlikDefaultContext, secrets, "qliksensePriv"))
|
||||
if err != nil {
|
||||
log.Fatalf("Could not delete private key %v", err)
|
||||
}
|
||||
return
|
||||
return targetFile, key, err
|
||||
}
|
||||
|
||||
func setup() func() {
|
||||
@@ -328,7 +244,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
||||
q: &Qliksense{
|
||||
QliksenseHome: testDir,
|
||||
},
|
||||
args: []string{"profile=minikube", "rotateKeys=yes", "storageClassName=efs", "gitops.enabled=yes", "gitops.schedule=30 * * * *", "git.repository=master", "git.username=foo", "git.accesstoken=1234"},
|
||||
args: []string{"profile=minikube", "rotateKeys=yes", "storageClassName=efs", "gitOps.enabled=yes", "gitOps.schedule=30 * * * *", "git.repository=master", "git.userName=foo", "git.accessToken=1234"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -338,7 +254,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
||||
q: &Qliksense{
|
||||
QliksenseHome: testDir,
|
||||
},
|
||||
args: []string{"someconfig=somevalue, gitops.schedule=bar", "gitops.enabled=bar", "git.foo=bar", "rotatekeys=bar"},
|
||||
args: []string{"someconfig=somevalue, gitOps.schedule=bar", "gitOps.enabled=bar", "git.foo=bar", "rotateKeys=bar"},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
@@ -389,7 +305,7 @@ func TestSetConfigs(t *testing.T) {
|
||||
defer tearDown()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := tt.args.q.SetConfigs(tt.args.args); (err != nil) != tt.wantErr {
|
||||
if err := tt.args.q.SetConfigs(tt.args.args, false); (err != nil) != tt.wantErr {
|
||||
t.Errorf("SetConfigs() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
@@ -508,9 +424,14 @@ spec:
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func removePrivateKey() {
|
||||
err := os.Remove(filepath.Join(testDir, secrets, contexts, qlikDefaultContext, secrets, "user_secret_key"))
|
||||
if err != nil {
|
||||
log.Fatalf("Could not delete private key %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
func Test_PrepareK8sSecret(t *testing.T) {
|
||||
|
||||
type fields struct {
|
||||
QliksenseHome string
|
||||
}
|
||||
@@ -530,7 +451,7 @@ func Test_PrepareK8sSecret(t *testing.T) {
|
||||
wantErr: false,
|
||||
setup: func() (string, func()) {
|
||||
tearDown := setup()
|
||||
_, _, targetFile, _ := setupTargetFileAndPrivateKey()
|
||||
targetFile, _, _ := setupTargetFileAndPrivateKey()
|
||||
return targetFile, tearDown
|
||||
},
|
||||
},
|
||||
@@ -543,7 +464,7 @@ func Test_PrepareK8sSecret(t *testing.T) {
|
||||
wantErr: true,
|
||||
setup: func() (string, func()) {
|
||||
tearDown := setup()
|
||||
_, _, targetFile, _ := setupTargetFileAndPrivateKey()
|
||||
targetFile, _, _ := setupTargetFileAndPrivateKey()
|
||||
removePrivateKey()
|
||||
return targetFile, tearDown
|
||||
},
|
||||
@@ -557,8 +478,7 @@ func Test_PrepareK8sSecret(t *testing.T) {
|
||||
wantErr: true,
|
||||
setup: func() (string, func()) {
|
||||
tearDown := setup()
|
||||
_, _, _, _ = setupTargetFileAndPrivateKey()
|
||||
removePrivateKey()
|
||||
setupTargetFileAndPrivateKey()
|
||||
return "", tearDown
|
||||
},
|
||||
},
|
||||
@@ -638,6 +558,7 @@ func Test_SetSecrets(t *testing.T) {
|
||||
type args struct {
|
||||
args []string
|
||||
isSecretSet bool
|
||||
base64 bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -656,6 +577,18 @@ func Test_SetSecrets(t *testing.T) {
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid secret secrets=false base64 encoded",
|
||||
fields: fields{
|
||||
QliksenseHome: testDir,
|
||||
},
|
||||
args: args{
|
||||
args: []string{"qliksense.mongoDbUri=bW9uZ29kYjovL3FsaWstZGVmYXVsdC1tb25nb2RiOjI3MDE3L3FsaWtzZW5zZT9zc2w9ZmFsc2U="},
|
||||
isSecretSet: false,
|
||||
base64: true,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "test1 valid secret secrets=true",
|
||||
fields: fields{
|
||||
@@ -691,22 +624,17 @@ func Test_SetSecrets(t *testing.T) {
|
||||
},
|
||||
}
|
||||
tearDown := setup()
|
||||
_, privateKeyBytes, _, err := setupTargetFileAndPrivateKey()
|
||||
_, encryptionKey, err := setupTargetFileAndPrivateKey()
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
privKey, err := api.DecodeToPrivateKey(privateKeyBytes)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
q := &Qliksense{
|
||||
QliksenseHome: tt.fields.QliksenseHome,
|
||||
}
|
||||
if err := q.SetSecrets(tt.args.args, tt.args.isSecretSet); (err != nil) != tt.wantErr {
|
||||
if err := q.SetSecrets(tt.args.args, tt.args.isSecretSet, tt.args.base64); (err != nil) != tt.wantErr {
|
||||
t.Errorf("SetSecrets() error = %v, wantErr %v", err, tt.wantErr)
|
||||
t.FailNow()
|
||||
}
|
||||
@@ -717,7 +645,10 @@ func Test_SetSecrets(t *testing.T) {
|
||||
// extract the value for testing
|
||||
testValueArr := strings.SplitN(tt.args.args[0], "=", 2)
|
||||
testValue := strings.ReplaceAll(testValueArr[1], "\"", "")
|
||||
|
||||
if tt.args.base64 {
|
||||
d, _ := b64.StdEncoding.DecodeString(testValue)
|
||||
testValue = strings.Trim(string(d), "\n ")
|
||||
}
|
||||
qliksenseCR, err := readCRFile()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Not able to read from context file: %v", err)
|
||||
@@ -734,13 +665,7 @@ func Test_SetSecrets(t *testing.T) {
|
||||
log.Printf("decode error: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
decodedValue, err := b64.StdEncoding.DecodeString(valToBeEncrypted)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error occurred while decoding: %v", err)
|
||||
log.Printf("decode error: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
decryptedVal, err := api.Decrypt(decodedValue, privKey)
|
||||
decryptedVal, err := api.DecryptData([]byte(valToBeEncrypted), encryptionKey)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error occurred while testing decryption: %v", err)
|
||||
log.Printf("No Data in Secret: %v", err)
|
||||
@@ -781,7 +706,8 @@ func getValueToBeDecodedForSetSecrets(item config.NameValue, qliksenseCR *api.Ql
|
||||
}
|
||||
// secret=false
|
||||
if item.Value != "" {
|
||||
return item.Value, nil
|
||||
b, err := b64.RawStdEncoding.DecodeString(item.Value)
|
||||
return string(b), err
|
||||
}
|
||||
err := fmt.Errorf("Both Value and ValueFrom are empty")
|
||||
return "", err
|
||||
@@ -929,9 +855,10 @@ func TestDeleteContexts(t *testing.T) {
|
||||
q := New(tt.args.qlikSenseHome)
|
||||
var arg []string
|
||||
arg = append(arg, tt.args.contextName)
|
||||
if err := q.DeleteContextConfig(arg); (err != nil) != tt.wantErr {
|
||||
if err := q.DeleteContextConfig(arg, true); (err != nil) != tt.wantErr {
|
||||
t.Errorf("DeleteContext() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package qliksense
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
@@ -85,6 +86,10 @@ func getQliksenseInitCrd(qcr *qapi.QliksenseCR) (string, error) {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -31,8 +31,10 @@ const (
|
||||
func (q *Qliksense) PullImages(version, profile string) error {
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
if version != "" {
|
||||
if err := q.FetchQK8s(version); err != nil {
|
||||
return err
|
||||
if !qConfig.IsRepoExistForCurrent(version) {
|
||||
if err := q.FetchQK8s(version); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
qcr, err := qConfig.GetCurrentCR()
|
||||
@@ -44,8 +46,8 @@ func (q *Qliksense) PullImages(version, profile string) error {
|
||||
}
|
||||
if profile != "" {
|
||||
qcr.Spec.Profile = profile
|
||||
if e := qConfig.WriteCR(qcr); e != nil {
|
||||
return e
|
||||
if err := qConfig.WriteCR(qcr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return q.PullImagesForCurrentCR()
|
||||
@@ -73,7 +75,7 @@ func (q *Qliksense) PullImagesForCurrentCR() error {
|
||||
}
|
||||
|
||||
images := versionOut.Images
|
||||
if err := q.appendOperatorImages(&images); err != nil {
|
||||
if err := q.appendAdditionalImages(&images, qcr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -93,6 +95,19 @@ func (q *Qliksense) PullImagesForCurrentCR() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) appendGitOpsImage(images *[]string, qcr *qapi.QliksenseCR) {
|
||||
if qcr.Spec.GitOps != nil && qcr.Spec.GitOps.Image != "" {
|
||||
*images = append(*images, qcr.Spec.GitOps.Image)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Qliksense) appendPreflightImages(images *[]string) {
|
||||
pf := qapi.NewPreflightConfig(q.QliksenseHome)
|
||||
for _, preflightImage := range pf.GetImageMap() {
|
||||
*images = append(*images, preflightImage)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Qliksense) appendOperatorImages(images *[]string) error {
|
||||
if operatorImages, err := getImageList([]byte(q.GetOperatorControllerString())); err != nil {
|
||||
return err
|
||||
@@ -140,7 +155,6 @@ func pullImage(image, imagesDir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TagAndPushImages ...
|
||||
func (q *Qliksense) PushImagesForCurrentCR() error {
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
qcr, err := qConfig.GetCurrentCR()
|
||||
@@ -155,7 +169,7 @@ func (q *Qliksense) PushImagesForCurrentCR() error {
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
dockerConfigJsonSecret = &qapi.DockerConfigJsonSecret{
|
||||
Uri: qcr.GetImageRegistry(),
|
||||
Uri: qcr.Spec.GetImageRegistry(),
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
@@ -173,7 +187,7 @@ func (q *Qliksense) PushImagesForCurrentCR() error {
|
||||
}
|
||||
|
||||
images := versionOut.Images
|
||||
if err := q.appendOperatorImages(&images); err != nil {
|
||||
if err := q.appendAdditionalImages(&images, qcr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -194,6 +208,15 @@ func (q *Qliksense) PushImagesForCurrentCR() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) appendAdditionalImages(images *[]string, qcr *qapi.QliksenseCR) error {
|
||||
if err := q.appendOperatorImages(images); err != nil {
|
||||
return err
|
||||
}
|
||||
q.appendGitOpsImage(images, qcr)
|
||||
q.appendPreflightImages(images)
|
||||
return nil
|
||||
}
|
||||
|
||||
func pushImage(image, imagesDir string, dockerConfigJsonSecret *qapi.DockerConfigJsonSecret) error {
|
||||
imageNameParts := getImageNameParts(image)
|
||||
srcDir := filepath.Join(imagesDir, imageIndexDirName, imageNameParts.name, imageNameParts.tag)
|
||||
|
||||
@@ -68,6 +68,9 @@ const (
|
||||
)
|
||||
|
||||
func Test_Pull_Push_ImagesForCurrentCR(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping pull/push tests in short mode")
|
||||
}
|
||||
var testCases = []struct {
|
||||
name string
|
||||
registryAuth bool
|
||||
@@ -131,7 +134,7 @@ func Test_Pull_Push_ImagesForCurrentCR(t *testing.T) {
|
||||
}
|
||||
q := &Qliksense{
|
||||
QliksenseHome: tmpQlikSenseHome,
|
||||
CrdBox: packr.New("crds", "./crds"),
|
||||
CrdBox: &packr.Box{},
|
||||
}
|
||||
var versionOut VersionOutput
|
||||
|
||||
@@ -170,29 +173,88 @@ func Test_Pull_Push_ImagesForCurrentCR(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func setupQlikSenseHome(t *testing.T, tmpQlikSenseHome string, registry *testRegistryV2, clientAuth clientAuthType) error {
|
||||
if err := ioutil.WriteFile(path.Join(tmpQlikSenseHome, "config.yaml"), []byte(`
|
||||
apiVersion: config.qlik.com/v1
|
||||
kind: QliksenseConfig
|
||||
func Test_appendAdditionalImages(t *testing.T) {
|
||||
tmpQlikSenseHome, err := ioutil.TempDir("", "tmp-qlik-sense-home-")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating tmp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpQlikSenseHome)
|
||||
|
||||
setupQliksenseTestDefaultContext(t, tmpQlikSenseHome, `
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
metadata:
|
||||
name: QliksenseConfigMetadata
|
||||
name: qlik-default
|
||||
spec:
|
||||
contexts:
|
||||
- name: qlik-default
|
||||
crFile: contexts/qlik-default/qlik-default.yaml
|
||||
currentContext: qlik-default
|
||||
`), os.ModePerm); err != nil {
|
||||
return err
|
||||
gitOps:
|
||||
image: some-gitops-image
|
||||
`)
|
||||
|
||||
q := &Qliksense{
|
||||
QliksenseHome: tmpQlikSenseHome,
|
||||
CrdBox: packr.New("crds", "./crds"),
|
||||
}
|
||||
|
||||
defaultContextDir := path.Join(tmpQlikSenseHome, "contexts", "qlik-default")
|
||||
if err := os.MkdirAll(defaultContextDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
pf := api.NewPreflightConfig(q.QliksenseHome)
|
||||
if err := pf.Initialize(); err != nil {
|
||||
t.Fatalf("unexpected error initializing preflight: %v", err)
|
||||
}
|
||||
|
||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||
qcr, err := qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting current CR: %v", err)
|
||||
}
|
||||
|
||||
images := make([]string, 0)
|
||||
if err := q.appendAdditionalImages(&images, qcr); err != nil {
|
||||
t.Fatalf("unexpected error appending additional images: %v", err)
|
||||
}
|
||||
|
||||
expectedNumberAdditionalImages := 5
|
||||
if len(images) != expectedNumberAdditionalImages {
|
||||
t.Fatalf("unexpected number of additional images: %v, expected: %v", len(images), expectedNumberAdditionalImages)
|
||||
}
|
||||
|
||||
haveMatchingImage := func(test func(string) bool) bool {
|
||||
for _, image := range images {
|
||||
if test(image) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if !haveMatchingImage(func(image string) bool {
|
||||
return strings.Contains(image, "qlik-docker-oss.bintray.io/qliksense-operator:v")
|
||||
}) {
|
||||
t.Fatal("expected to find the operator image in the list, but it wasn't there")
|
||||
}
|
||||
if !haveMatchingImage(func(image string) bool {
|
||||
return image == "some-gitops-image"
|
||||
}) {
|
||||
t.Fatal("expected to find the GitOps image in the list, but it wasn't there")
|
||||
}
|
||||
if !haveMatchingImage(func(image string) bool {
|
||||
return image == "nginx"
|
||||
}) {
|
||||
t.Fatal("expected to find the nginx Preflight image in the list, but it wasn't there")
|
||||
}
|
||||
if !haveMatchingImage(func(image string) bool {
|
||||
return image == "subfuzion/netcat"
|
||||
}) {
|
||||
t.Fatal("expected to find the netcat Preflight image in the list, but it wasn't there")
|
||||
}
|
||||
if !haveMatchingImage(func(image string) bool {
|
||||
return image == "mongo"
|
||||
}) {
|
||||
t.Fatal("expected to find the mongo Preflight image in the list, but it wasn't there")
|
||||
}
|
||||
}
|
||||
|
||||
func setupQlikSenseHome(t *testing.T, tmpQlikSenseHome string, registry *testRegistryV2, clientAuth clientAuthType) error {
|
||||
version := "foo"
|
||||
manifestsRootDir := fmt.Sprintf("%s/repo/%s", defaultContextDir, version)
|
||||
if err := ioutil.WriteFile(path.Join(defaultContextDir, "qlik-default.yaml"), []byte(fmt.Sprintf(`
|
||||
manifestsRootDir := filepath.ToSlash(path.Join(tmpQlikSenseHome, "contexts", "qlik-default", "repo", version))
|
||||
cr := fmt.Sprintf(`
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
metadata:
|
||||
@@ -208,9 +270,8 @@ spec:
|
||||
manifestsRoot: %s
|
||||
rotateKeys: "yes"
|
||||
releaseName: qlik-default
|
||||
`, version, registry.url, manifestsRootDir)), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
`, version, registry.url, manifestsRootDir)
|
||||
setupQliksenseTestDefaultContext(t, tmpQlikSenseHome, cr)
|
||||
|
||||
if clientAuth == clientAuthProvided || clientAuth == clientAuthProvidedButIncorrect {
|
||||
if registry.username == "" || clientAuth == clientAuthProvidedButIncorrect {
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
type FetchCommandOptions struct {
|
||||
GitUrl string
|
||||
AccessToken string
|
||||
Version string
|
||||
SecretName string
|
||||
Overwrite bool
|
||||
}
|
||||
|
||||
const (
|
||||
QLIK_GIT_REPO = "https://github.com/qlik-oss/qliksense-k8s"
|
||||
)
|
||||
@@ -17,24 +32,130 @@ func (q *Qliksense) FetchQK8s(version string) error {
|
||||
return fetchAndUpdateCR(qConfig, version)
|
||||
}
|
||||
|
||||
func (q *Qliksense) FetchK8sWithOpts(opts *FetchCommandOptions) error {
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
cr, err := qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.AccessToken != "" {
|
||||
encKey, err := qConfig.GetEncryptionKeyFor(cr.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cr.SetFetchAccessToken(opts.AccessToken, encKey); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if opts.SecretName != "" {
|
||||
cr.SetFetchAccessSecretName(opts.SecretName)
|
||||
}
|
||||
if opts.GitUrl != "" {
|
||||
cr.SetFetchUrl(opts.GitUrl)
|
||||
}
|
||||
v := getVersion(opts, cr)
|
||||
if v == "" {
|
||||
return errors.New("Cannot find gitref/tag/branch/version to fetch")
|
||||
}
|
||||
if qConfig.IsRepoExistForCurrent(v) {
|
||||
if opts.Overwrite || getVerionsOverwriteConfirmation(v) == "y" {
|
||||
if err := qConfig.DeleteRepoForCurrent(v); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// nothing to do
|
||||
return nil
|
||||
}
|
||||
}
|
||||
qConfig.WriteCR(cr)
|
||||
return fetchAndUpdateCR(qConfig, v)
|
||||
}
|
||||
|
||||
// fetchAndUpdateCR fetch
|
||||
func fetchAndUpdateCR(qConfig *qapi.QliksenseConfig, version string) error {
|
||||
qcr, err := qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
fmt.Println("cannot get the current-context cr", err)
|
||||
return err
|
||||
}
|
||||
if qConfig.IsRepoExistForCurrent(version) {
|
||||
return nil
|
||||
if version == "" {
|
||||
if qcr.GetLabelFromCr("version") == "" {
|
||||
if encKey, err := qConfig.GetEncryptionKeyFor(qcr.GetName()); err != nil {
|
||||
return err
|
||||
} else if version, err = getLatestTag(qcr.GetFetchUrl(), qcr.GetFetchAccessToken(encKey)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
version = qcr.GetLabelFromCr("version")
|
||||
}
|
||||
}
|
||||
encKey, err := qConfig.GetEncryptionKeyFor(qcr.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// downlaod to temp first
|
||||
tempDest, err := fetchToTempDir(qcr.GetFetchUrl(), version, qcr.GetFetchAccessToken(encKey))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
destDir := qConfig.BuildRepoPath(version)
|
||||
fmt.Printf("fetching version [%s] from %s\n", version, QLIK_GIT_REPO)
|
||||
|
||||
if repo, err := kapis_git.CloneRepository(destDir, QLIK_GIT_REPO, nil); err != nil {
|
||||
return err
|
||||
} else if err = kapis_git.Checkout(repo, version, fmt.Sprintf("%v-by-operator-%v", version, uuid.New().String()), nil); 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 {
|
||||
return nil
|
||||
}
|
||||
qcr.Spec.ManifestsRoot = qConfig.BuildCurrentManifestsRoot(version)
|
||||
qcr.AddLabelToCr("version", version)
|
||||
return qConfig.WriteCurrentContextCR(qcr)
|
||||
}
|
||||
|
||||
func fetchToTempDir(gitUrl, gitRef, accessToken string) (string, error) {
|
||||
tmpDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
downloadPath := path.Join(tmpDir, "repo")
|
||||
var auth transport.AuthMethod
|
||||
if accessToken != "" {
|
||||
auth = &http.BasicAuth{
|
||||
Username: "something",
|
||||
Password: accessToken,
|
||||
}
|
||||
}
|
||||
if repo, err := kapis_git.CloneRepository(downloadPath, gitUrl, auth); err != nil {
|
||||
return "", err
|
||||
} else if err := kapis_git.Checkout(repo, gitRef, "", auth); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return downloadPath, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getVersion(opts *FetchCommandOptions, qcr *qapi.QliksenseCR) string {
|
||||
if opts.Version == "" {
|
||||
if qcr.GetLabelFromCr("version") != "" {
|
||||
return qcr.GetLabelFromCr("version")
|
||||
}
|
||||
}
|
||||
return opts.Version
|
||||
}
|
||||
|
||||
func getVerionsOverwriteConfirmation(version string) string {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Println("The version [" + version + "] already exist")
|
||||
cfm := "n"
|
||||
for {
|
||||
fmt.Print("Do you want to delete and fetch again [y/N]: ")
|
||||
cfm, _ = reader.ReadString('\n')
|
||||
cfm = strings.Replace(cfm, "\n", "", -1)
|
||||
cfm = strings.TrimSpace(cfm)
|
||||
if cfm == "" {
|
||||
cfm = "n"
|
||||
}
|
||||
cfm = strings.ToLower(cfm)
|
||||
if cfm == "y" || cfm == "n" {
|
||||
break
|
||||
}
|
||||
}
|
||||
return cfm
|
||||
}
|
||||
|
||||
@@ -32,4 +32,19 @@ func TestFetchAndUpdateCR(t *testing.T) {
|
||||
t.Log("actual path: " + cr.Spec.ManifestsRoot + ", expected path: contexts/test1/qlik-k8s/v0.0.2")
|
||||
t.FailNow()
|
||||
}
|
||||
//testing latest tag is fetched
|
||||
cr.AddLabelToCr("version", "")
|
||||
qConfig.WriteCR(cr)
|
||||
err := fetchAndUpdateCR(qConfig, "")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
cr = &qapi.QliksenseCR{}
|
||||
qapi.ReadFromFile(cr, actualCrFile)
|
||||
v := cr.GetLabelFromCr("version")
|
||||
if v == "" || v == "v0.0.2" {
|
||||
t.Log("should get latest but got version: " + v)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/qlik-oss/k-apis/pkg/git"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
@@ -86,3 +87,69 @@ func (q *Qliksense) GetInstallableVersions(opts *LsRemoteCmdOptions) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLatestTag(repoUrl, accessToken string) (string, error) {
|
||||
if repoUrl == "" {
|
||||
return "", errors.New("repo url is empty")
|
||||
}
|
||||
repoPath, err := fetchToTempDir(repoUrl, "master", accessToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
r, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
remoteRefsList, err := git.GetRemoteRefs(r, nil,
|
||||
&git.RemoteRefConstraints{
|
||||
Include: true,
|
||||
Sort: true,
|
||||
SortOrder: git.RefSortOrderDescending,
|
||||
},
|
||||
&git.RemoteRefConstraints{
|
||||
Include: false,
|
||||
Sort: true,
|
||||
SortOrder: git.RefSortOrderAscending,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(remoteRefsList) < 1 {
|
||||
return "", errors.New("cannot find git remote information in the config repository")
|
||||
}
|
||||
|
||||
var originRemoteRefs *git.RemoteRefs
|
||||
for _, remoteRefs := range remoteRefsList {
|
||||
if remoteRefs.Name == "origin" {
|
||||
originRemoteRefs = remoteRefs
|
||||
break
|
||||
}
|
||||
}
|
||||
if originRemoteRefs == nil {
|
||||
return "", errors.New(`cannot find git remote called "origin" in the config repository`)
|
||||
}
|
||||
|
||||
tags := originRemoteRefs.Tags
|
||||
if len(tags) == 0 {
|
||||
return "", errors.New(("no tags exists in the repo: " + repoPath))
|
||||
}
|
||||
maxSem, _ := semver.NewVersion(tags[0])
|
||||
for _, sv := range tags[1:] {
|
||||
if sv == "" {
|
||||
continue
|
||||
}
|
||||
v, err := semver.NewVersion(sv)
|
||||
if err != nil {
|
||||
// it may happen, in the repo some tags may not conform to semver
|
||||
fmt.Print("Unconform tags: " + sv)
|
||||
continue
|
||||
}
|
||||
if maxSem == nil || maxSem.LessThan(v) {
|
||||
maxSem = v
|
||||
}
|
||||
}
|
||||
return maxSem.Original(), nil
|
||||
}
|
||||
|
||||
25
pkg/qliksense/get_installable_versions_test.go
Normal file
25
pkg/qliksense/get_installable_versions_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
)
|
||||
|
||||
func TestGetLatestTag(t *testing.T) {
|
||||
s, err := getLatestTag(defaultConfigRepoGitUrl, "")
|
||||
if s == "" || err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
sv, err := semver.NewVersion(s)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Log(sv)
|
||||
}
|
||||
baseV, _ := semver.NewVersion("v0.0.7")
|
||||
if !sv.GreaterThan(baseV) {
|
||||
t.Log("Expected greater than v0.0.7, but got: " + s)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@ package qliksense
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/qlik-oss/k-apis/pkg/config"
|
||||
@@ -27,11 +30,9 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
||||
// fetch the version
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
if !keepPatchFiles {
|
||||
defer func() {
|
||||
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
|
||||
fmt.Printf("error removing temporary changes to the config: %v\n", err)
|
||||
}
|
||||
}()
|
||||
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
|
||||
fmt.Printf("error removing temporary changes to the config: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
qcr, err := qConfig.GetCurrentCR()
|
||||
@@ -66,16 +67,11 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
||||
//CRD will be installed outside of operator
|
||||
//install operator controller into the namespace
|
||||
fmt.Println("Installing operator controller")
|
||||
operatorControllerString := q.GetOperatorControllerString()
|
||||
if imageRegistry := qcr.GetImageRegistry(); imageRegistry != "" {
|
||||
operatorControllerString, err = kustomizeForImageRegistry(operatorControllerString, pullSecretName,
|
||||
"qlik/qliksense-operator", fmt.Sprintf("%v/qliksense-operator", imageRegistry))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := qapi.KubectlApply(operatorControllerString, ""); err != nil {
|
||||
fmt.Println("cannot do kubectl apply on opeartor controller", err)
|
||||
if operatorControllerString, err := q.getProcessedOperatorControllerString(qcr); err != nil {
|
||||
fmt.Println("error extracting/transforming operator controller", err)
|
||||
return err
|
||||
} else if err := qapi.KubectlApply(operatorControllerString, ""); err != nil {
|
||||
fmt.Println("cannot do kubectl apply on operator controller", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -94,11 +90,10 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
||||
return q.applyCR(dcr)
|
||||
}
|
||||
}
|
||||
if version == "" {
|
||||
version = qcr.GetLabelFromCr("version")
|
||||
}
|
||||
if err := fetchAndUpdateCR(qConfig, version); err != nil {
|
||||
return err
|
||||
if !qcr.IsRepoExist() {
|
||||
if err := fetchAndUpdateCR(qConfig, version); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
qcr, err = qConfig.GetCurrentCR()
|
||||
@@ -114,17 +109,32 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
||||
|
||||
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 {
|
||||
return q.applyCR(dcr)
|
||||
if IsQliksenseInstalled(dcr.GetName()) {
|
||||
return q.UpgradeQK8s(keepPatchFiles)
|
||||
}
|
||||
if err := q.applyConfigToK8s(dcr); err != nil {
|
||||
fmt.Println("cannot do kubectl apply on manifests")
|
||||
return err
|
||||
} else {
|
||||
return q.applyCR(dcr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Qliksense) getProcessedOperatorControllerString(qcr *qapi.QliksenseCR) (string, error) {
|
||||
operatorControllerString := q.GetOperatorControllerString()
|
||||
if imageRegistry := qcr.Spec.GetImageRegistry(); imageRegistry != "" {
|
||||
return kustomizeForImageRegistry(operatorControllerString, pullSecretName,
|
||||
path.Join(qliksenseOperatorImageRepo, qliksenseOperatorImageName),
|
||||
path.Join(imageRegistry, qliksenseOperatorImageName))
|
||||
}
|
||||
return operatorControllerString, nil
|
||||
}
|
||||
|
||||
func installOrRemoveImagePullSecret(qConfig *qapi.QliksenseConfig) error {
|
||||
if pullDockerConfigJsonSecret, err := qConfig.GetPullDockerConfigJsonSecret(); err == nil {
|
||||
if dockerConfigJsonSecretYaml, err := pullDockerConfigJsonSecret.ToYaml(nil); err != nil {
|
||||
if dockerConfigJsonSecretYaml, err := pullDockerConfigJsonSecret.ToYaml(""); err != nil {
|
||||
return err
|
||||
} else if err := qapi.KubectlApply(string(dockerConfigJsonSecretYaml), ""); err != nil {
|
||||
return err
|
||||
@@ -133,7 +143,7 @@ func installOrRemoveImagePullSecret(qConfig *qapi.QliksenseConfig) error {
|
||||
deleteDockerConfigJsonSecret := qapi.DockerConfigJsonSecret{
|
||||
Name: pullSecretName,
|
||||
}
|
||||
if deleteDockerConfigJsonSecretYaml, err := deleteDockerConfigJsonSecret.ToYaml(nil); err != nil {
|
||||
if deleteDockerConfigJsonSecretYaml, err := deleteDockerConfigJsonSecret.ToYaml(""); err != nil {
|
||||
return err
|
||||
} else if err := qapi.KubectlDelete(string(deleteDockerConfigJsonSecretYaml), ""); err != nil {
|
||||
qapi.LogDebugMessage("failed deleting %v, error: %v\n", pullSecretName, err)
|
||||
@@ -143,10 +153,15 @@ func installOrRemoveImagePullSecret(qConfig *qapi.QliksenseConfig) error {
|
||||
}
|
||||
|
||||
func kustomizeForImageRegistry(resources, dockerConfigJsonSecretName, name, newName string) (string, error) {
|
||||
fSys := filesys.MakeFsInMemory()
|
||||
if err := fSys.WriteFile("/resources.yaml", []byte(resources)); err != nil {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if err := fSys.WriteFile("/addImagePullSecrets.yaml", []byte(fmt.Sprintf(`
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "resources.yaml"), []byte(resources), os.ModePerm); err != nil {
|
||||
return "", err
|
||||
} else if err := ioutil.WriteFile(filepath.Join(dir, "addImagePullSecrets.yaml"), []byte(fmt.Sprintf(`
|
||||
apiVersion: builtin
|
||||
kind: PatchTransformer
|
||||
metadata:
|
||||
@@ -155,9 +170,9 @@ patch: '[{"op": "add", "path": "/spec/template/spec/imagePullSecrets", "value":
|
||||
target:
|
||||
name: .*-operator
|
||||
kind: Deployment
|
||||
`, dockerConfigJsonSecretName))); err != nil {
|
||||
`, dockerConfigJsonSecretName)), os.ModePerm); err != nil {
|
||||
return "", err
|
||||
} else if err := fSys.WriteFile("/kustomization.yaml", []byte(fmt.Sprintf(`
|
||||
} else if err := ioutil.WriteFile(filepath.Join(dir, "kustomization.yaml"), []byte(fmt.Sprintf(`
|
||||
resources:
|
||||
- resources.yaml
|
||||
transformers:
|
||||
@@ -165,9 +180,9 @@ transformers:
|
||||
images:
|
||||
- name: %s
|
||||
newName: %s
|
||||
`, name, newName))); err != nil {
|
||||
`, name, newName)), os.ModePerm); err != nil {
|
||||
return "", err
|
||||
} else if out, err := executeKustomizeBuildForFileSystem("/", fSys); err != nil {
|
||||
} else if out, err := executeKustomizeBuildForFileSystem(dir, filesys.MakeFsOnDisk()); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return string(out), nil
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -63,3 +72,105 @@ spec:
|
||||
}
|
||||
td()
|
||||
}
|
||||
|
||||
func setupQliksenseTestDefaultContext(t *testing.T, tmpQlikSenseHome, CR string) {
|
||||
if err := ioutil.WriteFile(path.Join(tmpQlikSenseHome, "config.yaml"), []byte(`
|
||||
apiVersion: config.qlik.com/v1
|
||||
kind: QliksenseConfig
|
||||
metadata:
|
||||
name: QliksenseConfigMetadata
|
||||
spec:
|
||||
contexts:
|
||||
- name: qlik-default
|
||||
crFile: contexts/qlik-default/qlik-default.yaml
|
||||
currentContext: qlik-default
|
||||
`), os.ModePerm); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
defaultContextDir := path.Join(tmpQlikSenseHome, "contexts", "qlik-default")
|
||||
if err := os.MkdirAll(defaultContextDir, os.ModePerm); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path.Join(defaultContextDir, "qlik-default.yaml"), []byte(CR), os.ModePerm); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getProcessedOperatorControllerString(t *testing.T) {
|
||||
tmpQlikSenseHome, err := ioutil.TempDir("", "tmp-qlik-sense-home-")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating tmp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpQlikSenseHome)
|
||||
|
||||
registry := "registryFoo"
|
||||
setupQliksenseTestDefaultContext(t, tmpQlikSenseHome, fmt.Sprintf(`
|
||||
apiVersion: qlik.com/v1
|
||||
kind: Qliksense
|
||||
metadata:
|
||||
name: qlik-default
|
||||
spec:
|
||||
configs:
|
||||
qliksense:
|
||||
- name: imageRegistry
|
||||
value: %v
|
||||
`, registry))
|
||||
|
||||
q := &Qliksense{
|
||||
QliksenseHome: tmpQlikSenseHome,
|
||||
CrdBox: packr.New("crds", "./crds"),
|
||||
}
|
||||
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
qcr, err := qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting current CR: %v", err)
|
||||
}
|
||||
|
||||
originalOperatorString := q.GetOperatorControllerString()
|
||||
processedOperatorString, err := q.getProcessedOperatorControllerString(qcr)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
controllerImageChecks := map[string]func(t *testing.T, controllerImage string){
|
||||
originalOperatorString: func(t *testing.T, controllerImage string) {
|
||||
expectedControllerImagePrefix := fmt.Sprintf("%v/%v:", qliksenseOperatorImageRepo, qliksenseOperatorImageName)
|
||||
if !strings.HasPrefix(controllerImage, expectedControllerImagePrefix) {
|
||||
t.Fatalf("expected controller image: %v to have prefix: %v", controllerImage, expectedControllerImagePrefix)
|
||||
}
|
||||
},
|
||||
processedOperatorString: func(t *testing.T, controllerImage string) {
|
||||
expectedControllerImagePrefix := fmt.Sprintf("%v/%v:", registry, qliksenseOperatorImageName)
|
||||
if !strings.HasPrefix(controllerImage, expectedControllerImagePrefix) {
|
||||
t.Fatalf("expected controller image: %v to have prefix: %v", controllerImage, expectedControllerImagePrefix)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
resourceFactory := resmap.NewFactory(resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()), nil)
|
||||
for operatorString, controllerImageCheck := range controllerImageChecks {
|
||||
resMap, err := resourceFactory.NewResMapFromBytes([]byte(operatorString))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
res, err := resMap.GetById(resid.NewResId(resid.Gvk{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Deployment",
|
||||
}, "qliksense-operator"))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
controllerImage, err := res.GetString("spec.template.spec.containers[0].image")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
controllerImageCheck(t, controllerImage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func GetYamlsFromMultiDoc(multiYaml string, kind string) string {
|
||||
for _, doc := range yamlDocs {
|
||||
scanner := bufio.NewScanner(strings.NewReader(doc))
|
||||
for scanner.Scan() {
|
||||
if strings.TrimSpace(scanner.Text()) == "kind: "+kind {
|
||||
if scanner.Text() == "kind: "+kind {
|
||||
resultDocs = resultDocs + "\n---\n" + doc
|
||||
break
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
|
||||
configPath := path.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, "v1.21.23-edge", "", nil); err != nil {
|
||||
} else if err := kapis_git.Checkout(repo, "e38df644e759abf0b5941c1511d1a2cd5e3c42fa", "", nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v\n", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ func (q *Qliksense) loadCrStringIntoFileSystem(crstr string, overwriteExistingCo
|
||||
}
|
||||
|
||||
// encrypt the secrets and do base64 then update the CR
|
||||
rsaPublicKey, _, err := qConfig.GetContextEncryptionKeyPair(cr.GetName())
|
||||
encryptionKey, err := qConfig.GetEncryptionKeyFor(cr.GetName())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -63,13 +63,17 @@ func (q *Qliksense) loadCrStringIntoFileSystem(crstr string, overwriteExistingCo
|
||||
Value: nv.Value,
|
||||
SvcName: svc,
|
||||
}
|
||||
if err := q.processSecret(skv, rsaPublicKey, cr, false); err != nil {
|
||||
if err := q.processSecret(skv, encryptionKey, cr, false); err != nil {
|
||||
return cr.GetName(), err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cr.Spec.FetchSource != nil && cr.Spec.FetchSource.AccessToken != "" {
|
||||
if err := cr.SetFetchAccessToken(cr.Spec.FetchSource.AccessToken, encryptionKey); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
// update manifestsRoot in case already exist
|
||||
if existingCr, err := qConfig.GetCR(cr.GetName()); err == nil {
|
||||
// cr exists, so update the manifestsRoot if version exist
|
||||
|
||||
@@ -6,16 +6,24 @@ import (
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
func (q *Qliksense) UninstallQK8s(contextName string) error {
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
if contextName == "" {
|
||||
contextName = qConfig.Spec.CurrentContext
|
||||
} else if !qConfig.IsContextExist(contextName) {
|
||||
return errors.New("context name [ " + contextName + " ] not found")
|
||||
func (q *Qliksense) UninstallQK8s(contextName string, skipConfirmation bool) error {
|
||||
ans := skipConfirmation
|
||||
|
||||
if ans == false {
|
||||
ans = AskForConfirmation("Are You Sure? ")
|
||||
}
|
||||
str, err := q.getCRString(contextName)
|
||||
if err != nil {
|
||||
return err
|
||||
if ans == true {
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
if contextName == "" {
|
||||
contextName = qConfig.Spec.CurrentContext
|
||||
} else if !qConfig.IsContextExist(contextName) {
|
||||
return errors.New("context name [ " + contextName + " ] not found")
|
||||
}
|
||||
str, err := q.getCRString(contextName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return qapi.KubectlDelete(str, "")
|
||||
}
|
||||
return qapi.KubectlDelete(str, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user