Compare commits
33 Commits
fix-go-git
...
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 | ||
|
|
6093552ba9 | ||
|
|
449642e6f4 | ||
|
|
97b6cf21a7 | ||
|
|
14b6447154 | ||
|
|
1c60ce4cc0 | ||
|
|
7a8926773f | ||
|
|
0b868732a7 | ||
|
|
4f2581cde2 | ||
|
|
cb78b4da9f |
39
.github/workflows/build.yml
vendored
39
.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: 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
|
||||
|
||||
33
Makefile
33
Makefile
@@ -39,18 +39,18 @@ 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-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
|
||||
@@ -86,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}),)
|
||||
@@ -93,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,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,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,330 +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
|
||||
}
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
qp.RunAllPreflightChecks(namespace, kubeConfigContents, mongodbUrl)
|
||||
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 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
|
||||
},
|
||||
}
|
||||
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 namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
if err = qp.CheckMongo(kubeConfigContents, namespace, mongodbUrl); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Print("Preflight mongo check FAILED\n")
|
||||
log.Fatal()
|
||||
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.Spec.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
|
||||
}
|
||||
|
||||
@@ -27,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 {
|
||||
@@ -85,17 +92,20 @@ 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)
|
||||
@@ -108,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)
|
||||
@@ -162,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))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
10
go.mod
10
go.mod
@@ -26,6 +26,7 @@ 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
|
||||
@@ -36,32 +37,31 @@ require (
|
||||
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.1.0
|
||||
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
|
||||
github.com/spf13/viper v1.6.1
|
||||
github.com/src-d/go-git v4.7.0+incompatible
|
||||
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
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
gopkg.in/yaml.v3 v3.0.0-20190924164351-c8b7dadae555
|
||||
k8s.io/api v0.17.0
|
||||
k8s.io/apimachinery v0.17.0
|
||||
k8s.io/client-go v11.0.0+incompatible
|
||||
k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51
|
||||
sigs.k8s.io/kustomize/api v0.3.2
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
||||
18
go.sum
18
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=
|
||||
@@ -864,6 +876,8 @@ github.com/qlik-oss/k-apis v0.0.39 h1:fIGCC7f9kU7319VTSJKr3fLoA9E4MjusRFmOjX3ypi
|
||||
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=
|
||||
@@ -1066,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=
|
||||
@@ -1146,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=
|
||||
@@ -1309,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
|
||||
|
||||
@@ -109,6 +109,7 @@ func ReadFromFile(content interface{}, sourceFile string) error {
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer file.Close()
|
||||
return ReadFromStream(content, file)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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...)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,106 +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 == totalCount {
|
||||
fmt.Printf("\nAll preflight checks have PASSED\n")
|
||||
} else {
|
||||
fmt.Printf("\n1 or more preflight checks have FAILED\n")
|
||||
// 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,18 +48,17 @@ 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
|
||||
}
|
||||
|
||||
@@ -72,38 +71,38 @@ func (qp *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namesp
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pod, err := createPreflightTestPod(clientset, namespace, podName, imageName, commandToRun)
|
||||
pod, err := qp.createPreflightTestPod(clientset, namespace, podName, imageName, nil, commandToRun)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create pod %s - %v\n", podName, err)
|
||||
err = fmt.Errorf("unable to create pod - %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer deletePod(clientset, namespace, podName)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -113,16 +112,16 @@ func (qp *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pfDeployment, err := createPreflightTestDeployment(clientset, namespace, depName, imageName)
|
||||
pfDeployment, err := qp.createPreflightTestDeployment(clientset, namespace, depName, imageName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create deployment: %v\n", err)
|
||||
err = fmt.Errorf("unable to create deployment - %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer deleteDeployment(clientset, namespace, depName)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@ const (
|
||||
)
|
||||
|
||||
func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byte) error {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -24,13 +25,12 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dnsDeployment, err := createPreflightTestDeployment(clientset, namespace, depName, nginxImageName)
|
||||
dnsDeployment, err := qp.createPreflightTestDeployment(clientset, namespace, depName, nginxImageName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create deployment: %v\n", err)
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("unable to create deployment: %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer deleteDeployment(clientset, namespace, depName)
|
||||
defer qp.deleteDeployment(clientset, namespace, depName)
|
||||
|
||||
if err := waitForDeployment(clientset, namespace, dnsDeployment); err != nil {
|
||||
return err
|
||||
@@ -38,34 +38,34 @@ 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"
|
||||
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("unable to retrieve image : %v\n", err)
|
||||
return err
|
||||
}
|
||||
dnsPod, err := createPreflightTestPod(clientset, namespace, podName, netcatImageName, commandToRun)
|
||||
dnsPod, err := qp.createPreflightTestPod(clientset, namespace, podName, netcatImageName, nil, commandToRun)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create pod : %s\n", podName)
|
||||
err = fmt.Errorf("unable to create pod : %s, %s\n", podName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer deletePod(clientset, namespace, podName)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -73,20 +73,19 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by
|
||||
|
||||
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(strings.TrimSpace(logStr), "succeeded!") {
|
||||
fmt.Println("Preflight DNS check: PASSED")
|
||||
qp.P.LogVerboseMessage("Preflight DNS check: PASSED\n")
|
||||
} else {
|
||||
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,21 +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 (
|
||||
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
|
||||
|
||||
@@ -24,64 +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 := qp.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 (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespace, mongodbUrl string) error {
|
||||
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"
|
||||
commandToRun := []string{"sh", "-c", "sleep 10;mongo " + mongodbUrl}
|
||||
imageName, err := qp.GetPreflightConfigObj().GetImageName(mongo, true)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to retrieve image : %v\n", err)
|
||||
return err
|
||||
}
|
||||
mongoPod, err := createPreflightTestPod(clientset, namespace, podName, imageName, commandToRun)
|
||||
mongoPod, err := qp.createPreflightTestPod(clientset, namespace, podName, imageName, secrets, commandToRun)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to create pod : %v\n", err)
|
||||
err = fmt.Errorf("unable to create pod : %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer deletePod(clientset, namespace, podName)
|
||||
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- %v\n", err)
|
||||
fmt.Println(err)
|
||||
err := fmt.Errorf("there are no containers in the pod- %v\n", err)
|
||||
return err
|
||||
}
|
||||
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\n", err)
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("unable to execute mongo check in the cluster: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
stringToCheck := "Implicit session:"
|
||||
if strings.Contains(logStr, stringToCheck) {
|
||||
fmt.Println("Preflight mongo check: PASSED")
|
||||
qp.P.LogVerboseMessage("Preflight mongo check: PASSED\n")
|
||||
} else {
|
||||
err = fmt.Errorf("Expected response not found\n")
|
||||
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
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -26,10 +27,32 @@ import (
|
||||
"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() *api.PreflightConfig {
|
||||
@@ -93,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 != "" {
|
||||
@@ -109,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{
|
||||
@@ -167,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
|
||||
}
|
||||
@@ -183,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
|
||||
@@ -202,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{
|
||||
@@ -240,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
|
||||
}
|
||||
@@ -256,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
|
||||
@@ -276,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
|
||||
@@ -291,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, commandToRun []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{
|
||||
@@ -323,16 +337,40 @@ func createPreflightTestPod(clientset *kubernetes.Clientset, namespace string, p
|
||||
},
|
||||
},
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -397,8 +435,7 @@ func waitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDepl
|
||||
checkFunc := func() (interface{}, error) {
|
||||
pfDeployment, err = getDeployment(clientset, namespace, depName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to retrieve deployment: %s\n", depName)
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("unable to retrieve deployment: %s\n", depName)
|
||||
return nil, err
|
||||
}
|
||||
return pfDeployment, nil
|
||||
@@ -411,8 +448,7 @@ func waitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDepl
|
||||
return err
|
||||
}
|
||||
if int(pfDeployment.Status.ReadyReplicas) == 0 {
|
||||
err = fmt.Errorf("error: deployment took longer than expected to spin up pods")
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("deployment took longer than expected to spin up pods")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -421,16 +457,14 @@ func waitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDepl
|
||||
func waitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error {
|
||||
var err error
|
||||
if len(pod.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
|
||||
}
|
||||
podName := pod.Name
|
||||
checkFunc := func() (interface{}, error) {
|
||||
pod, err = getPod(clientset, namespace, podName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to retrieve %s pod by name", podName)
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("unable to retrieve %s pod by name", podName)
|
||||
return nil, err
|
||||
}
|
||||
return pod, nil
|
||||
@@ -444,8 +478,7 @@ func waitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Po
|
||||
return err
|
||||
}
|
||||
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)
|
||||
err = fmt.Errorf("container is taking much longer than expected")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -456,8 +489,7 @@ func waitForPodToDie(clientset *kubernetes.Clientset, namespace string, pod *api
|
||||
checkFunc := func() (interface{}, error) {
|
||||
po, err := getPod(clientset, namespace, podName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error: unable to retrieve %s pod by name", podName)
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("unable to retrieve %s pod by name", podName)
|
||||
return nil, err
|
||||
}
|
||||
api.LogDebugMessage("pod status: %v\n", po.Status.Phase)
|
||||
@@ -487,8 +519,7 @@ func waitForPodToDelete(clientset *kubernetes.Clientset, namespace, podName stri
|
||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||
return nil
|
||||
}
|
||||
err := fmt.Errorf("error: delete pod is taking unusually long")
|
||||
fmt.Println(err)
|
||||
err := fmt.Errorf("delete pod is taking unusually long")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -506,12 +537,11 @@ func waitForDeploymentToDelete(clientset *kubernetes.Clientset, namespace, deplo
|
||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||
return nil
|
||||
}
|
||||
err := fmt.Errorf("error: delete deployment is taking unusually long")
|
||||
fmt.Println(err)
|
||||
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{
|
||||
@@ -530,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
|
||||
@@ -550,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{
|
||||
@@ -561,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{
|
||||
@@ -584,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
|
||||
@@ -602,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{
|
||||
@@ -613,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",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -623,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
|
||||
@@ -641,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,6 +5,7 @@ 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"
|
||||
@@ -14,34 +15,37 @@ 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 {
|
||||
@@ -52,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
|
||||
@@ -72,7 +76,7 @@ func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string)
|
||||
if len(resultYamlBytes) == 0 {
|
||||
resultYamlBytes, err = qliksense.ExecuteKustomizeBuild(kusDir)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to retrieve manifests from executing kustomize: %v\n", err)
|
||||
err := fmt.Errorf("Unable to retrieve manifests from executing kustomize: %s, error: %v", kusDir, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -80,58 +84,74 @@ func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string)
|
||||
if sa != "" {
|
||||
sa = strings.Replace(sa, "name: qliksense", "name: preflight", -1)
|
||||
} else {
|
||||
err := fmt.Errorf("Unable to retrieve yamls to apply on cluster")
|
||||
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() {
|
||||
fmt.Println("Cleaning up resources")
|
||||
api.KubectlDelete(sa, namespace)
|
||||
qp.P.LogVerboseMessage("Cleaning up resources...\n")
|
||||
err := api.KubectlDeleteVerbose(sa, namespace, qp.P.Verbose)
|
||||
if err != nil {
|
||||
fmt.Println("Preflight cleanup failed!")
|
||||
qp.P.LogVerboseMessage("Preflight cleanup failed!\n")
|
||||
}
|
||||
}()
|
||||
|
||||
err = api.KubectlApply(sa, namespace)
|
||||
err = api.KubectlApplyVerbose(sa, namespace, qp.P.Verbose)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Failed to create entity on the cluster: %v", err)
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Preflight %s check: PASSED\n", entityToTest)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,19 +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 {
|
||||
v := cr.GetLabelFromCr("version")
|
||||
if !qConfig.IsRepoExistForCurrent(v) {
|
||||
if err := q.FetchQK8s(v); err != nil {
|
||||
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 {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -403,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
|
||||
@@ -445,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
|
||||
|
||||
@@ -855,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)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -46,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()
|
||||
@@ -155,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()
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
"github.com/src-d/go-git/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
|
||||
)
|
||||
|
||||
type FetchCommandOptions struct {
|
||||
@@ -80,9 +80,14 @@ func fetchAndUpdateCR(qConfig *qapi.QliksenseConfig, version string) error {
|
||||
}
|
||||
if version == "" {
|
||||
if qcr.GetLabelFromCr("version") == "" {
|
||||
return errors.New("Cannot find gitref/tag/branch/version to fetch")
|
||||
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")
|
||||
}
|
||||
version = qcr.GetLabelFromCr("version")
|
||||
}
|
||||
encKey, err := qConfig.GetEncryptionKeyFor(qcr.GetName())
|
||||
if err != nil {
|
||||
|
||||
@@ -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,8 @@ package qliksense
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
@@ -107,11 +109,16 @@ 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,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:
|
||||
@@ -158,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:
|
||||
@@ -168,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
|
||||
|
||||
@@ -130,7 +130,6 @@ spec:
|
||||
}
|
||||
|
||||
originalOperatorString := q.GetOperatorControllerString()
|
||||
|
||||
processedOperatorString, err := q.getProcessedOperatorControllerString(qcr)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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