Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
775f438762 | ||
|
|
aa180b4af1 | ||
|
|
af679c89bf | ||
|
|
dcd3c0a99b | ||
|
|
19c4d37b42 | ||
|
|
dcd90ed81a | ||
|
|
05e90c057c | ||
|
|
2ddfab9440 | ||
|
|
2bc65f0bad | ||
|
|
1a2de669ba | ||
|
|
b2a980de3a | ||
|
|
bfba8198cf | ||
|
|
86e8805bc7 | ||
|
|
c2430c3817 | ||
|
|
2f039f2d2e | ||
|
|
48ee673ddc | ||
|
|
57a80a9533 | ||
|
|
4fe04d6142 | ||
|
|
1fd3310e05 | ||
|
|
b85269d908 | ||
|
|
cbdafadbaf | ||
|
|
c0e2128d5d | ||
|
|
c4f0ddcea3 | ||
|
|
f57457029d | ||
|
|
aa737b0594 | ||
|
|
b7c0fd48b7 | ||
|
|
4530d1d9e2 | ||
|
|
ca20f8c992 | ||
|
|
b2c16a490b | ||
|
|
7f70cc661e | ||
|
|
2c054cd54e | ||
|
|
30f00461ec | ||
|
|
613b918dde |
4
.github/workflows/mkdocs.yml
vendored
4
.github/workflows/mkdocs.yml
vendored
@@ -4,8 +4,8 @@ on:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- docs/
|
||||
- mkdocs.yml
|
||||
- 'docs/**'
|
||||
- 'mkdocs.yml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@ pkg/qliksense/packrd
|
||||
pkg/qliksense/qliksense-packr.go
|
||||
pkg/qliksense/docker-registry
|
||||
/pkg/qliksense/tests
|
||||
.DS_Store
|
||||
|
||||
28
Makefile
28
Makefile
@@ -46,11 +46,11 @@ build: clean generate
|
||||
.PHONY: test
|
||||
test: clean generate
|
||||
ifeq ($(shell ${WHICH} docker-registry 2>${DEVNUL}),)
|
||||
$(eval TMP := $(shell mktemp -d))
|
||||
git clone https://github.com/docker/distribution.git $(TMP)/docker-distribution
|
||||
cd $(TMP)/docker-distribution; git checkout -b v2.7.1; make
|
||||
cp $(TMP)/docker-distribution/bin/registry pkg/qliksense/docker-registry
|
||||
-rm -rf $(TMP)/docker-distribution
|
||||
$(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)
|
||||
endif
|
||||
go test -short -count=1 -tags "$(BUILDTAGS)" -v ./...
|
||||
$(MAKE) clean
|
||||
@@ -70,7 +70,7 @@ $(BINDIR)/$(VERSION)/$(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH)$(FILE_EXT):
|
||||
GOOS=$(CLIENT_PLATFORM) GOARCH=$(CLIENT_ARCH) $(XBUILD) -o $@ ./cmd/$(MIXIN)
|
||||
|
||||
ifeq ($(CLIENT_PLATFORM),windows)
|
||||
zip $(BINDIR)/$(VERSION)/$(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH).zip $(BINDIR)/$(VERSION)/ $(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH)$(FILE_EXT)
|
||||
zip $(BINDIR)/$(VERSION)/$(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH).zip $(BINDIR)/$(VERSION)/$(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH)$(FILE_EXT)
|
||||
else
|
||||
tar -czvf $(BINDIR)/$(VERSION)/$(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH).tar.gz -C $(BINDIR)/$(VERSION)/ $(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH)$(FILE_EXT)
|
||||
endif
|
||||
@@ -91,12 +91,16 @@ clean-packr: packr2
|
||||
cd pkg/qliksense && packr2 clean
|
||||
|
||||
get-crds:
|
||||
$(eval TMP := $(shell mktemp -d))
|
||||
git clone https://github.com/qlik-oss/qliksense-operator.git -b ms-3 $(TMP)/operator
|
||||
ifeq ($(QLIKSENSE_OPERATOR_DIR),)
|
||||
$(eval TMP-operator := $(shell mktemp -d))
|
||||
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)
|
||||
else
|
||||
mkdir -p pkg/qliksense/crds/cr
|
||||
mkdir -p pkg/qliksense/crds/crd
|
||||
mkdir -p pkg/qliksense/crds/crd-deploy
|
||||
cp $(TMP)/operator/deploy/*.yaml pkg/qliksense/crds/crd-deploy
|
||||
cp $(TMP)/operator/deploy/crds/*_crd.yaml pkg/qliksense/crds/crd
|
||||
cp $(TMP)/operator/deploy/crds/*_cr.yaml pkg/qliksense/crds/cr
|
||||
-rm -rf $(TMP)/operator
|
||||
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
|
||||
endif
|
||||
|
||||
@@ -20,7 +20,7 @@ func configCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
func configApplyCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "apply",
|
||||
Short: "generate the patchs and apply manifests to k8s",
|
||||
Short: "generate the patches and apply manifests to k8s",
|
||||
Long: `generate patches based on CR and apply manifests to k8s`,
|
||||
Example: `qliksense config apply`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
47
cmd/qliksense/eula.go
Normal file
47
cmd/qliksense/eula.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
)
|
||||
|
||||
var eulaEnforced = false
|
||||
var eulaText = "EULA text goes here..."
|
||||
var eulaPrompt = "Do you accept our EULA? (y/n): "
|
||||
var eulaErrorInstruction = "You must enter y/yes to continue"
|
||||
|
||||
func isEulaEnforced() bool {
|
||||
return eulaEnforced
|
||||
}
|
||||
|
||||
func enforceEula(q *qliksense.Qliksense) {
|
||||
if isEulaEnforced() {
|
||||
if qConfig, err := qapi.NewQConfigE(q.QliksenseHome); err != nil {
|
||||
doEnforceEula()
|
||||
} else if qcr, err := qConfig.GetCurrentCR(); err != nil || !qcr.IsEULA() {
|
||||
doEnforceEula()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func doEnforceEula() {
|
||||
fmt.Println(eulaText)
|
||||
fmt.Print(eulaPrompt)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
scanSuccess := scanner.Scan()
|
||||
if !scanSuccess {
|
||||
fmt.Println(eulaErrorInstruction)
|
||||
os.Exit(1)
|
||||
}
|
||||
line := scanner.Text()
|
||||
answer := strings.ToLower(strings.TrimSpace(line))
|
||||
if answer != "y" && answer != "yes" {
|
||||
fmt.Println(eulaErrorInstruction)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
29
cmd/qliksense/get_installable_versions.go
Normal file
29
cmd/qliksense/get_installable_versions.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const defaultVersionsLimit = 10
|
||||
|
||||
func getInstallableVersionsCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
opts := &qliksense.LsRemoteCmdOptions{
|
||||
IncludeBranches: false,
|
||||
Limit: defaultVersionsLimit,
|
||||
}
|
||||
c := &cobra.Command{
|
||||
Use: "get-versions",
|
||||
Short: "list remote/installable versions",
|
||||
Long: `list remote/installable versions`,
|
||||
Example: `qliksense get-versions`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return q.GetInstallableVersions(opts)
|
||||
},
|
||||
}
|
||||
|
||||
f := c.Flags()
|
||||
f.BoolVarP(&opts.IncludeBranches, "include-branches", "", opts.IncludeBranches, "Include branches")
|
||||
f.IntVarP(&opts.Limit, "limit", "", opts.Limit, "Maximum versions to list (starting with the highest)")
|
||||
return c
|
||||
}
|
||||
41
cmd/qliksense/preflight.go
Normal file
41
cmd/qliksense/preflight.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func preflightCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
var configCmd = &cobra.Command{
|
||||
Use: "preflight",
|
||||
Short: "perform preflight checks on the cluster",
|
||||
Long: `perform preflight checks on the cluster`,
|
||||
Example: `qliksense preflight <preflight_check_to_run>
|
||||
Usage:
|
||||
qliksense preflight dns
|
||||
`,
|
||||
}
|
||||
return configCmd
|
||||
}
|
||||
|
||||
func preflightCheckDnsCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
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 {
|
||||
err := q.DownloadPreflight()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("There has been an error downloading preflight: %+v", err)
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
return q.CheckDns()
|
||||
},
|
||||
}
|
||||
return preflightDnsCmd
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -19,7 +18,7 @@ import (
|
||||
"github.com/ttacon/chalk"
|
||||
)
|
||||
|
||||
// To run this project in ddebug mode, run:
|
||||
// To run this project in debug mode, run:
|
||||
// export QLIKSENSE_DEBUG=true
|
||||
// qliksense <command>
|
||||
|
||||
@@ -43,14 +42,13 @@ func initAndExecute() error {
|
||||
api.LogDebugMessage("QliksenseHomeDir: %s", qlikSenseHome)
|
||||
|
||||
qliksenseClient := qliksense.New(qlikSenseHome)
|
||||
qliksenseClient.SetUpQliksenseDefaultContext()
|
||||
cmd := rootCmd(qliksenseClient)
|
||||
//levenstein checks
|
||||
if levenstein(cmd) == false {
|
||||
if err := cmd.Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Execute(); err != nil {
|
||||
//levenstein checks (auto-suggestions)
|
||||
levenstein(cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -86,16 +84,37 @@ var versionCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
var (
|
||||
cmd *cobra.Command
|
||||
)
|
||||
func commandUsesContext(command string) bool {
|
||||
return command != "" && command != "help" && command != "version"
|
||||
}
|
||||
|
||||
cmd = &cobra.Command{
|
||||
func globalPreRun(cmd *cobra.Command, p *qliksense.Qliksense) {
|
||||
if command := cmd.CalledAs(); commandUsesContext(command) {
|
||||
if isEulaEnforced() {
|
||||
enforceEula(p)
|
||||
}
|
||||
|
||||
if err := p.SetUpQliksenseDefaultContext(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if isEulaEnforced() {
|
||||
if err := p.SetEulaAccepted(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "qliksense",
|
||||
Short: "Qliksense cli tool",
|
||||
Long: `qliksense cli tool provides functionality to perform operations on qliksense-k8s, qliksense operator, and kubernetes cluster`,
|
||||
Args: cobra.ArbitraryArgs,
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
globalPreRun(cmd, p)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().SetInterspersed(false)
|
||||
@@ -104,6 +123,7 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
|
||||
// For qliksense overrides/commands
|
||||
|
||||
cmd.AddCommand(getInstallableVersionsCmd(p))
|
||||
cmd.AddCommand(pullQliksenseImages(p))
|
||||
cmd.AddCommand(pushQliksenseImages(p))
|
||||
cmd.AddCommand(about(p))
|
||||
@@ -164,6 +184,15 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||
cmd.AddCommand(crdsCmd)
|
||||
crdsCmd.AddCommand(crdsViewCmd(p))
|
||||
crdsCmd.AddCommand(crdsInstallCmd(p))
|
||||
|
||||
// add preflight command
|
||||
preflightCmd := preflightCmd(p)
|
||||
preflightCmd.AddCommand(preflightCheckDnsCmd(p))
|
||||
//preflightCmd.AddCommand(preflightCheckMongoCmd(p))
|
||||
//preflightCmd.AddCommand(preflightCheckAllCmd(p))
|
||||
|
||||
cmd.AddCommand(preflightCmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -172,32 +201,6 @@ func initConfig() {
|
||||
viper.AutomaticEnv()
|
||||
}
|
||||
|
||||
func downloadFile(url string, filepath string) error {
|
||||
var (
|
||||
out *os.File
|
||||
err error
|
||||
resp *http.Response
|
||||
)
|
||||
// Create the file
|
||||
if out, err = os.Create(filepath); err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// Get the data
|
||||
if resp, err = http.Get(url); err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Write the body to file
|
||||
if _, err = io.Copy(out, resp.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copy(src, dst string) (int64, error) {
|
||||
var (
|
||||
source, destination *os.File
|
||||
@@ -226,28 +229,21 @@ func copy(src, dst string) (int64, error) {
|
||||
return nBytes, err
|
||||
}
|
||||
|
||||
func levenstein(cmd *cobra.Command) bool {
|
||||
cmd.SuggestionsMinimumDistance = 4
|
||||
func levenstein(cmd *cobra.Command) {
|
||||
cmd.SuggestionsMinimumDistance = 2
|
||||
if len(os.Args) > 1 {
|
||||
args := os.Args[1]
|
||||
for _, ctx := range cmd.Commands() {
|
||||
val := *ctx
|
||||
if args == val.Name() {
|
||||
//found command
|
||||
return false
|
||||
}
|
||||
}
|
||||
suggest := cmd.SuggestionsFor(os.Args[1])
|
||||
suggest := cmd.SuggestionsFor(args)
|
||||
if len(suggest) > 0 {
|
||||
arg := []string{}
|
||||
for _, cm := range os.Args {
|
||||
arg = append(arg, cm)
|
||||
}
|
||||
arg[1] = suggest[0]
|
||||
out := ansi.NewColorableStdout()
|
||||
fmt.Fprintln(out, chalk.Green.Color("Did you mean: "), chalk.Bold.TextStyle(strings.Join(arg, " ")), "?")
|
||||
return true
|
||||
if !strings.EqualFold(arg[1], suggest[0]) {
|
||||
arg[1] = suggest[0]
|
||||
out := ansi.NewColorableStdout()
|
||||
fmt.Fprintln(out, chalk.Green.Color("Did you mean: "), chalk.Bold.TextStyle(strings.Join(arg, " ")), "?")
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -9,7 +8,7 @@ import (
|
||||
func uninstallCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "uninstall",
|
||||
Short: "Uninstall the deployed qliksense with release name [ " + qapi.NewQConfig(q.QliksenseHome).Spec.CurrentContext + " ]",
|
||||
Short: "Uninstall the deployed qliksense.",
|
||||
Long: `Uninstall the deployed qliksense. By default uninstall the current context`,
|
||||
Example: `qliksense uninstall <context-name>`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
@@ -75,3 +75,56 @@ When you perform `qliksense install` or `qliksene config apply`, qliksense opera
|
||||
- Install kubernetes resources
|
||||
- Push generated patches into a new branch in the provided git repo. _Gives you ability to merge patches into your master branch_
|
||||
- Create a CronJob to monitor master branch. Any changes pushed to master branch will be applied into the cluster. _This is a light weight `git-ops` model_
|
||||
|
||||
## GitOps
|
||||
|
||||
To enable gitops, the following section should be in the CR
|
||||
|
||||
```yaml
|
||||
....
|
||||
spec:
|
||||
git:
|
||||
repository: https://github.com/<OWNER>/<REPO>
|
||||
accessToken: "<git-token>"
|
||||
userName: "<git-username>"
|
||||
gitOps:
|
||||
enabled: "yes"
|
||||
schedule: "*/5 * * * *"
|
||||
watchBranch: <myBranch>
|
||||
image: qlik-docker-oss.bintray.io/qliksense-repo-watcher
|
||||
....
|
||||
```
|
||||
|
||||
##Preflight checks
|
||||
Preflight checks provide pre-installation cluster conformance testing and validation before we install qliksense on the cluster. We gather a suite of conformance tests that can be easily written and run on the target cluster to verify that cluster-specific requirements are met.
|
||||
The suite consists of a set of `collectors` which run the specifications of every test and `analyzers` which analyze the results of every test run by the collector.
|
||||
We support the following tests at the moment as part of preflight checks, and the range of the suite will be expanded in future.
|
||||
|
||||
### DNS check
|
||||
Run the following command to view help about the commands supported by preflight at any moment:
|
||||
```console
|
||||
qliksense preflight
|
||||
perform preflight checks on the cluster
|
||||
|
||||
Usage:
|
||||
qliksense preflight [command]
|
||||
|
||||
Examples:
|
||||
qliksense preflight <preflight_check_to_run>
|
||||
|
||||
Usage:
|
||||
qliksense preflight dns
|
||||
|
||||
Available Commands:
|
||||
dns perform preflight dns check
|
||||
```
|
||||
|
||||
Run the following command to perform preflight DNS check. The expected output is also shown below.
|
||||
```console
|
||||
qliksense preflight dns
|
||||
Running Preflight checks ⠧
|
||||
--- PASS DNS check
|
||||
--- DNS check passed
|
||||
--- PASS cluster-preflight-checks
|
||||
PASS
|
||||
```
|
||||
|
||||
9
go.mod
9
go.mod
@@ -39,22 +39,25 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.4
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/qlik-oss/k-apis v0.0.19
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/qlik-oss/k-apis v0.0.22
|
||||
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/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 // indirect
|
||||
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/tools v0.0.0-20200309202150-20ab64c0d93f // indirect
|
||||
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/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
|
||||
sigs.k8s.io/kustomize/api v0.3.2
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
||||
19
go.sum
19
go.sum
@@ -843,17 +843,16 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
|
||||
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
|
||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/qlik-oss/k-apis v0.0.16/go.mod h1:KOFzKVIdRqp47ytnHg3+9zb8fTlnrQjO6aKiwcrCJUE=
|
||||
github.com/qlik-oss/k-apis v0.0.17 h1:tOdrEe9gfb9CXq0+uowFnXIsI781qz/zgeN8xqupXYw=
|
||||
github.com/qlik-oss/k-apis v0.0.17/go.mod h1:KOFzKVIdRqp47ytnHg3+9zb8fTlnrQjO6aKiwcrCJUE=
|
||||
github.com/qlik-oss/k-apis v0.0.19 h1:yrMgALQ08vMDi5hN6fnvIfyNsEaXA5fZjB1YhyIdTfg=
|
||||
github.com/qlik-oss/k-apis v0.0.19/go.mod h1:DNiWYqCqPIN216l7+1rccArNIYPb1Le7kYDcPSyNp+Q=
|
||||
github.com/qlik-oss/k-apis v0.0.22 h1:tntQEeRqDYkBi2Ku5+xt7ABGMeFPck7+DOKrHUnpzwI=
|
||||
github.com/qlik-oss/k-apis v0.0.22/go.mod h1:DNiWYqCqPIN216l7+1rccArNIYPb1Le7kYDcPSyNp+Q=
|
||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200206224201-2e697eccbad9 h1:iqeqTS4zjp6rPEaxmFB7pemA2CMjOEN5dYSXZaQ82uw=
|
||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200206224201-2e697eccbad9/go.mod h1:OCt7FTrRVHj4kmR2xLJJUIqu00BTr6GeF09hSmM17Kw=
|
||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
|
||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@@ -1043,10 +1042,8 @@ 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 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/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=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1208,8 +1205,8 @@ golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c h1:2EA2K0k9bcvvEDlqD8xdlOhCOqq+O/p9Voqi4x9W1YU=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200309202150-20ab64c0d93f h1:NbrfHxef+IfdI86qCgO/1Siq1BuMH2xG0NqgvCguRhQ=
|
||||
golang.org/x/tools v0.0.0-20200309202150-20ab64c0d93f/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312194400-c312e98713c2 h1:6TB4+MaZlkcSsJDu+BS5yxSEuZIYhjWz+jhbSLEZylI=
|
||||
golang.org/x/tools v0.0.0-20200312194400-c312e98713c2/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
BIN
pkg/.DS_Store
vendored
Normal file
BIN
pkg/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -27,16 +27,24 @@ const (
|
||||
|
||||
// NewQConfig create QliksenseConfig object from file ~/.qliksense/config.yaml
|
||||
func NewQConfig(qsHome string) *QliksenseConfig {
|
||||
qc, err := NewQConfigE(qsHome)
|
||||
if err != nil {
|
||||
fmt.Println("yaml unmarshalling error ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return qc
|
||||
}
|
||||
|
||||
func NewQConfigE(qsHome string) (*QliksenseConfig, error) {
|
||||
configFile := filepath.Join(qsHome, "config.yaml")
|
||||
qc := &QliksenseConfig{}
|
||||
|
||||
err := ReadFromFile(qc, configFile)
|
||||
if err != nil {
|
||||
fmt.Println("yaml unmarshalling error ", err)
|
||||
os.Exit(1)
|
||||
return nil, err
|
||||
}
|
||||
qc.QliksenseHomePath = qsHome
|
||||
return qc
|
||||
return qc, nil
|
||||
}
|
||||
|
||||
// GetCR create a QliksenseCR object for a particular context
|
||||
@@ -342,6 +350,10 @@ func (cr *QliksenseCR) IsEULA() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (cr *QliksenseCR) SetEULA(value string) {
|
||||
cr.Spec.AddToConfigs("qliksense", "acceptEULA", value)
|
||||
}
|
||||
|
||||
// GetDecryptedCr it decrypts all the encrypted value and return a new CR
|
||||
func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error) {
|
||||
newCr := &QliksenseCR{}
|
||||
|
||||
@@ -97,3 +97,21 @@ func kubectlOperation(manifests string, oprName string, namespace string) error
|
||||
os.Remove(tempYaml.Name())
|
||||
return nil
|
||||
}
|
||||
|
||||
func KubectlDirectOps(opr []string, namespace string) error {
|
||||
arguments := []string{}
|
||||
if namespace != "" {
|
||||
arguments = append(arguments, "-n", namespace)
|
||||
}
|
||||
arguments = append(arguments, opr...)
|
||||
|
||||
cmd := exec.Command("kubectl", arguments...)
|
||||
LogDebugMessage("Kubectl command: %s %v\n", "kubectl", arguments)
|
||||
sterrBuffer := &bytes.Buffer{}
|
||||
cmd.Stderr = sterrBuffer
|
||||
cmd.Stdout = os.Stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("kubectl %v failed with: %v, %v\n", opr, err, sterrBuffer.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -15,3 +17,16 @@ func TestGetKubectlNamespace(t *testing.T) {
|
||||
}
|
||||
SetKubectlNamespace(ns)
|
||||
}
|
||||
|
||||
func TestKubectlDirectOps(t *testing.T) {
|
||||
t.Skip()
|
||||
SetKubectlNamespace("test")
|
||||
ns := GetKubectlNamespace()
|
||||
opr := fmt.Sprintf("version")
|
||||
opr1 := strings.Fields(opr)
|
||||
err := KubectlDirectOps(opr1, ns)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
157
pkg/api/utils.go
157
pkg/api/utils.go
@@ -1,13 +1,21 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func checkExists(filename string) os.FileInfo {
|
||||
@@ -109,3 +117,152 @@ func ExecuteTaskWithBlinkingStdoutFeedback(task func() (interface{}, error), fee
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DownloadFile(url, baseFolder, installerName string) error {
|
||||
var (
|
||||
out *os.File
|
||||
err error
|
||||
resp *http.Response
|
||||
)
|
||||
// Create the file
|
||||
fileName := filepath.Join(baseFolder, installerName)
|
||||
LogDebugMessage("Installer Filename: %s\n", fileName)
|
||||
if out, err = os.Create(fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// Get the data
|
||||
if resp, err = http.Get(url); err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("unable to download the file from URL: %s, status: %s", url, resp.Status)
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Write the body to file
|
||||
if _, err = io.Copy(out, resp.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chmod(fileName, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ExplodePackage(destination, fileToUntar string) error {
|
||||
LogDebugMessage("Destination: %s\n", destination)
|
||||
LogDebugMessage("fileToUntar: %s\n", fileToUntar)
|
||||
|
||||
if strings.HasSuffix(fileToUntar, "zip") {
|
||||
LogDebugMessage("This is a windows file : %s", fileToUntar)
|
||||
err := UnZipFile(destination, fileToUntar)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
} else if strings.HasSuffix(fileToUntar, "tar.gz") {
|
||||
LogDebugMessage("This is a mac/linux file: %s", fileToUntar)
|
||||
err := UntarGzFile(destination, fileToUntar)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UntarGzFile(destination, fileToUntar string) error {
|
||||
lFile, err := os.Open(fileToUntar)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "unable to read the local file %s", fileToUntar)
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
gzReader, err := gzip.NewReader(lFile)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "unable to load the file into a gz reader")
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
defer gzReader.Close()
|
||||
|
||||
tarReader := tar.NewReader(gzReader)
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
return nil
|
||||
case err != nil:
|
||||
err = errors.Wrap(err, "error during untar")
|
||||
log.Fatal(err)
|
||||
return err
|
||||
case header == nil:
|
||||
continue
|
||||
}
|
||||
|
||||
fileInLoop := filepath.Join(destination, header.Name)
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
if _, err := os.Stat(fileInLoop); err != nil {
|
||||
if err := os.MkdirAll(fileInLoop, 0755); err != nil {
|
||||
err = errors.Wrapf(err, "error creating directory %s", fileInLoop)
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
case tar.TypeReg:
|
||||
fileAtLoc, err := os.OpenFile(fileInLoop, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "error opening file %s", fileInLoop)
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(fileAtLoc, tarReader); err != nil {
|
||||
err = errors.Wrapf(err, "error writing file %s", fileInLoop)
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
fileAtLoc.Close()
|
||||
fileAtLoc.Chmod(os.ModePerm)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UnZipFile(destination, fileToUnzip string) error {
|
||||
zipReader, _ := zip.OpenReader(fileToUnzip)
|
||||
for _, file := range zipReader.Reader.File {
|
||||
|
||||
zippedFile, err := file.Open()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer zippedFile.Close()
|
||||
extractedFilePath := filepath.Join(
|
||||
destination,
|
||||
file.Name,
|
||||
)
|
||||
outputFile, err := os.OpenFile(
|
||||
extractedFilePath,
|
||||
os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
|
||||
file.Mode(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer outputFile.Close()
|
||||
|
||||
_, err = io.Copy(outputFile, zippedFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
LogDebugMessage("File extracted: %s, Extracted file path: %s\n", file.Name, extractedFilePath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
BIN
pkg/qliksense/.DS_Store
vendored
Normal file
BIN
pkg/qliksense/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -10,9 +10,12 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/qlik-oss/k-apis/pkg/config"
|
||||
|
||||
b64 "encoding/base64"
|
||||
|
||||
ansi "github.com/mattn/go-colorable"
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
"github.com/ttacon/chalk"
|
||||
@@ -149,7 +152,6 @@ func (q *Qliksense) SetConfigs(args []string) error {
|
||||
func retrieveCurrentContextInfo(q *Qliksense) (*api.QliksenseCR, string, error) {
|
||||
var qliksenseConfig api.QliksenseConfig
|
||||
qliksenseConfigFile := filepath.Join(q.QliksenseHome, QliksenseConfigFile)
|
||||
|
||||
if err := api.ReadFromFile(&qliksenseConfig, qliksenseConfigFile); err != nil {
|
||||
log.Println(err)
|
||||
return nil, "", err
|
||||
@@ -225,8 +227,42 @@ func (q *Qliksense) SetOtherConfigs(args []string) error {
|
||||
}
|
||||
qliksenseCR.Spec.RotateKeys = rotateKeys
|
||||
api.LogDebugMessage("Current rotateKeys after modification: %s ", qliksenseCR.Spec.RotateKeys)
|
||||
case "gitops.enabled":
|
||||
if qliksenseCR.Spec.GitOps == nil {
|
||||
qliksenseCR.Spec.GitOps = &config.GitOps{}
|
||||
}
|
||||
if strings.EqualFold(argsString[1], "yes") || strings.EqualFold(argsString[1], "no") {
|
||||
qliksenseCR.Spec.GitOps.Enabled = argsString[1]
|
||||
api.LogDebugMessage("Current gitOps enabled status : %s ", qliksenseCR.Spec.GitOps.Enabled)
|
||||
} else {
|
||||
err := fmt.Errorf("Please use yes or no")
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
case "gitops.schedule":
|
||||
if qliksenseCR.Spec.GitOps == nil {
|
||||
qliksenseCR.Spec.GitOps = &config.GitOps{}
|
||||
}
|
||||
if _, err := cron.ParseStandard(argsString[1]); err != nil {
|
||||
err := fmt.Errorf("Please enter string with standard cron scheduling syntax ")
|
||||
return err
|
||||
}
|
||||
qliksenseCR.Spec.GitOps.Schedule = argsString[1]
|
||||
api.LogDebugMessage("Current gitOps schedule is : %s ", qliksenseCR.Spec.GitOps.Schedule)
|
||||
case "gitops.watchbranch":
|
||||
if qliksenseCR.Spec.GitOps == nil {
|
||||
qliksenseCR.Spec.GitOps = &config.GitOps{}
|
||||
}
|
||||
qliksenseCR.Spec.GitOps.WatchBranch = argsString[1]
|
||||
api.LogDebugMessage("Current gitOps watchbranch is : %s ", qliksenseCR.Spec.GitOps.WatchBranch)
|
||||
case "gitops.image":
|
||||
if qliksenseCR.Spec.GitOps == nil {
|
||||
qliksenseCR.Spec.GitOps = &config.GitOps{}
|
||||
}
|
||||
qliksenseCR.Spec.GitOps.Image = argsString[1]
|
||||
api.LogDebugMessage("Current gitOps image is : %s ", qliksenseCR.Spec.GitOps.Image)
|
||||
default:
|
||||
err := fmt.Errorf("Please enter one of: profile, storageClassName,rotateKeys, manifestRoot or git.repository arguments to configure the current context")
|
||||
err := fmt.Errorf("Please enter one of: profile, storageClassName,rotateKeys, manifestRoot, git.repository or gitops arguments to configure the current context")
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
@@ -543,3 +579,13 @@ func (q *Qliksense) SetImageRegistry(registry, pushUsername, pushPassword, pullU
|
||||
qliksenseCR.Spec.AddToConfigs("qliksense", imageRegistryConfigKey, registry)
|
||||
return api.WriteToFile(&qliksenseCR, qliksenseContextsFile)
|
||||
}
|
||||
|
||||
func (q *Qliksense) SetEulaAccepted() error {
|
||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||
qcr, err := qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
qcr.SetEULA("yes")
|
||||
return qConfig.WriteCurrentContextCR(qcr)
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
||||
q: &Qliksense{
|
||||
QliksenseHome: testDir,
|
||||
},
|
||||
args: []string{"profile=minikube", "rotateKeys=yes", "storageClassName=efs"},
|
||||
args: []string{"profile=minikube", "rotateKeys=yes", "storageClassName=efs", "gitops.enabled=yes", "gitops.schedule=30 * * * *"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -336,7 +336,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
||||
q: &Qliksense{
|
||||
QliksenseHome: testDir,
|
||||
},
|
||||
args: []string{"someconfig=somevalue"},
|
||||
args: []string{"someconfig=somevalue, gitops.schedule=bar", "gitops.enabled=bar"},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
88
pkg/qliksense/get_installable_versions.go
Normal file
88
pkg/qliksense/get_installable_versions.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/qlik-oss/k-apis/pkg/git"
|
||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
type LsRemoteCmdOptions struct {
|
||||
IncludeBranches bool
|
||||
Limit int
|
||||
}
|
||||
|
||||
func (q *Qliksense) GetInstallableVersions(opts *LsRemoteCmdOptions) error {
|
||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||
qcr, err := qConfig.GetCurrentCR()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var repoPath string
|
||||
if qcr.Spec.GetManifestsRoot() != "" {
|
||||
repoPath = qcr.Spec.GetManifestsRoot()
|
||||
} else {
|
||||
repoPath, err = downloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
|
||||
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: opts.IncludeBranches,
|
||||
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) > opts.Limit {
|
||||
tags = tags[:opts.Limit]
|
||||
}
|
||||
fmt.Print("Versions:\n")
|
||||
for _, tag := range tags {
|
||||
fmt.Printf(" %s\n", tag)
|
||||
}
|
||||
if opts.IncludeBranches {
|
||||
branches := originRemoteRefs.Branches
|
||||
if len(branches) > opts.Limit {
|
||||
branches = branches[:opts.Limit]
|
||||
}
|
||||
fmt.Print("Branches:\n")
|
||||
for _, branch := range branches {
|
||||
fmt.Printf(" %s\n", branch)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
||||
}
|
||||
|
||||
if opts.AcceptEULA != "" {
|
||||
qcr.Spec.AddToConfigs("qliksense", "acceptEULA", opts.AcceptEULA)
|
||||
qcr.SetEULA(opts.AcceptEULA)
|
||||
}
|
||||
if opts.MongoDbUri != "" {
|
||||
qcr.Spec.AddToSecrets("qliksense", "mongoDbUri", opts.MongoDbUri, "")
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp" // don't delete this line ref: https://github.com/kubernetes/client-go/issues/242
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
"sigs.k8s.io/kustomize/api/krusty"
|
||||
|
||||
301
pkg/qliksense/preflight_checks.go
Normal file
301
pkg/qliksense/preflight_checks.go
Normal file
@@ -0,0 +1,301 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"text/template"
|
||||
|
||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||
)
|
||||
|
||||
const (
|
||||
// preflight releases have the same version
|
||||
preflightRelease = "v0.9.26"
|
||||
preflightLinuxFile = "preflight_linux_amd64.tar.gz"
|
||||
preflightMacFile = "preflight_darwin_amd64.tar.gz"
|
||||
preflightWindowsFile = "preflight_windows_amd64.zip"
|
||||
PreflightChecksDirName = "preflight_checks"
|
||||
)
|
||||
|
||||
var preflightBaseURL = fmt.Sprintf("https://github.com/replicatedhq/troubleshoot/releases/download/%s/", preflightRelease)
|
||||
|
||||
const dnsCheckYAML = `
|
||||
apiVersion: troubleshoot.replicated.com/v1beta1
|
||||
kind: Preflight
|
||||
metadata:
|
||||
name: cluster-preflight-checks
|
||||
namespace: {{ . }}
|
||||
spec:
|
||||
collectors:
|
||||
- run:
|
||||
collectorName: spin-up-pod
|
||||
args: ["-z", "-v", "-w 1", "qnginx001", "80"]
|
||||
command: ["nc"]
|
||||
image: subfuzion/netcat:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: spin-up-pod-check-dns
|
||||
namespace: {{ . }}
|
||||
timeout: 30s
|
||||
|
||||
analyzers:
|
||||
- textAnalyze:
|
||||
checkName: DNS check
|
||||
collectorName: spin-up-pod-check-dns
|
||||
fileName: spin-up-pod.txt
|
||||
regex: succeeded
|
||||
outcomes:
|
||||
- fail:
|
||||
message: DNS check failed
|
||||
- pass:
|
||||
message: DNS check passed
|
||||
`
|
||||
|
||||
func (q *Qliksense) DownloadPreflight() error {
|
||||
const preflightExecutable = "preflight"
|
||||
|
||||
preflightInstallDir := filepath.Join(q.QliksenseHome, PreflightChecksDirName)
|
||||
platform := runtime.GOOS
|
||||
|
||||
exists, err := checkInstalled(preflightInstallDir, preflightExecutable)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("There has been an error when trying to determine if preflight installer exists")
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
// preflight exist, no need to download again.
|
||||
api.LogDebugMessage("Preflight already exist, proceeding to perform checks")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create the Preflight-check directory, download and install preflight
|
||||
if !api.DirExists(preflightInstallDir) {
|
||||
api.LogDebugMessage("%s does not exist, creating now\n", preflightInstallDir)
|
||||
if err := os.Mkdir(preflightInstallDir, os.ModePerm); err != nil {
|
||||
err = fmt.Errorf("Not able to create %s dir: %v", preflightInstallDir, err)
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
api.LogDebugMessage("Preflight-checks install Dir: %s exists", preflightInstallDir)
|
||||
|
||||
preflightUrl, preflightFile, err := determinePlatformSpecificUrls(platform)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("There was an error when trying to determine platform specific paths")
|
||||
return err
|
||||
}
|
||||
|
||||
// Download Preflight
|
||||
err = downloadAndExplode(preflightUrl, preflightInstallDir, preflightFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Downloaded Preflight")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkInstalled(preflightInstallDir, preflightExecutable string) (bool, error) {
|
||||
installerExists := true
|
||||
preflightInstaller := filepath.Join(preflightInstallDir, preflightExecutable)
|
||||
if api.DirExists(preflightInstallDir) {
|
||||
if !api.FileExists(preflightInstaller) {
|
||||
installerExists = false
|
||||
api.LogDebugMessage("Preflight install directory exists, but preflight installer does not exist")
|
||||
}
|
||||
} else {
|
||||
installerExists = false
|
||||
}
|
||||
return installerExists, nil
|
||||
}
|
||||
|
||||
func downloadAndExplode(url, installDir, file string) error {
|
||||
err := api.DownloadFile(url, installDir, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
api.LogDebugMessage("Downloaded File: %s", file)
|
||||
|
||||
fileToUntar := filepath.Join(installDir, file)
|
||||
api.LogDebugMessage("File to explode: %s", file)
|
||||
|
||||
err = api.ExplodePackage(installDir, fileToUntar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func determinePlatformSpecificUrls(platform string) (string, string, error) {
|
||||
|
||||
var preflightUrl, preflightFile string
|
||||
|
||||
if runtime.GOARCH != `amd64` {
|
||||
err := fmt.Errorf("%s architecture is not supported", runtime.GOARCH)
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
switch platform {
|
||||
case "windows":
|
||||
preflightFile = preflightWindowsFile
|
||||
case "darwin":
|
||||
preflightFile = preflightMacFile
|
||||
case "linux":
|
||||
preflightFile = preflightLinuxFile
|
||||
default:
|
||||
err := fmt.Errorf("Unable to download the preflight executable for the underlying platform\n")
|
||||
return "", "", err
|
||||
}
|
||||
preflightUrl = fmt.Sprintf("%s%s", preflightBaseURL, preflightFile)
|
||||
|
||||
return preflightUrl, preflightFile, nil
|
||||
}
|
||||
|
||||
func (q *Qliksense) CheckDns() error {
|
||||
// retrieve namespace
|
||||
namespace := api.GetKubectlNamespace()
|
||||
|
||||
api.LogDebugMessage("Namespace: %s\n", namespace)
|
||||
|
||||
tmpl, err := template.New("test").Parse(dnsCheckYAML)
|
||||
if err != nil {
|
||||
fmt.Printf("cannot parse template: %v", err)
|
||||
return err
|
||||
}
|
||||
tempYaml, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
fmt.Printf("cannot create file: %v", err)
|
||||
return err
|
||||
}
|
||||
api.LogDebugMessage("Temp Yaml file: %s\n", tempYaml.Name())
|
||||
|
||||
b := bytes.Buffer{}
|
||||
err = tmpl.Execute(&b, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
tempYaml.WriteString(b.String())
|
||||
|
||||
// creating Kubectl resources
|
||||
appName := "qnginx001"
|
||||
const PreflightChecksDirName = "preflight_checks"
|
||||
const preflightFileName = "preflight"
|
||||
|
||||
fmt.Println("Creating resources to run preflight checks")
|
||||
|
||||
// kubectl create deployment
|
||||
opr := fmt.Sprintf("create deployment %s --image=nginx", appName)
|
||||
err = initiateK8sOps(opr, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Deleting deployment..
|
||||
opr = fmt.Sprintf("delete deployment %s", appName)
|
||||
// we want to delete the k8s resource here, we dont care a lot about an error here
|
||||
_ = initiateK8sOps(opr, namespace)
|
||||
api.LogDebugMessage("delete deployment executed")
|
||||
}()
|
||||
|
||||
// create service
|
||||
opr = fmt.Sprintf("create service clusterip %s --tcp=80:80", appName)
|
||||
err = initiateK8sOps(opr, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// delete service
|
||||
opr = fmt.Sprintf("delete service %s", appName)
|
||||
// we want to delete the k8s resource here, we dont care a lot about an error here
|
||||
_ = initiateK8sOps(opr, namespace)
|
||||
api.LogDebugMessage("delete service executed")
|
||||
}()
|
||||
|
||||
//kubectl -n $namespace wait --for=condition=ready pod -l app=$appName --timeout=120s
|
||||
opr = fmt.Sprintf("wait --for=condition=ready pod -l app=%s --timeout=120s", appName)
|
||||
err = initiateK8sOps(opr, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
api.LogDebugMessage("kubectl wait executed")
|
||||
|
||||
// call preflight
|
||||
preflightCommand := filepath.Join(q.QliksenseHome, PreflightChecksDirName, preflightFileName)
|
||||
|
||||
err = invokePreflight(preflightCommand, tempYaml)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initiateK8sOps(opr, namespace string) error {
|
||||
opr1 := strings.Fields(opr)
|
||||
err := api.KubectlDirectOps(opr1, namespace)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func invokePreflight(preflightCommand string, yamlFile *os.File) error {
|
||||
arguments := []string{}
|
||||
arguments = append(arguments, yamlFile.Name(), "--interactive=false")
|
||||
cmd := exec.Command(preflightCommand, arguments...)
|
||||
|
||||
sterrBuffer := &bytes.Buffer{}
|
||||
cmd.Stdout = sterrBuffer
|
||||
cmd.Stderr = sterrBuffer
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("Error when running preflight command: %v\n", err)
|
||||
}
|
||||
ind := strings.Index(sterrBuffer.String(), "---")
|
||||
output := sterrBuffer.String()
|
||||
if ind > -1 {
|
||||
output = fmt.Sprintf("%s\n%s", output[:ind], output[ind:])
|
||||
}
|
||||
fmt.Printf("%v\n", output)
|
||||
|
||||
// Maybe good to retain this part in case we need to process the output in future.
|
||||
// We are going to look for the first occurance of PASS or FAIL from the end
|
||||
// there are also some space-like deceiving characters which are being hard to get by
|
||||
|
||||
//outputArr := strings.Fields(strings.TrimSpace(output))
|
||||
//trackSuccess := false
|
||||
//trackPrg := false
|
||||
|
||||
//for i := len(outputArr) - 1; i >= 0; i-- {
|
||||
// if strings.TrimSpace(outputArr[i]) != "" {
|
||||
// if outputArr[i] == "PASS" {
|
||||
// trackSuccess = true
|
||||
// trackPrg = true
|
||||
// } else if outputArr[i] == "FAIL" {
|
||||
// trackPrg = true
|
||||
// }
|
||||
// }
|
||||
// if trackPrg {
|
||||
// break
|
||||
// }
|
||||
//}
|
||||
fmt.Println("Preflight checks completed, cleaning up resources now")
|
||||
return nil
|
||||
}
|
||||
90
pkg/qliksense/preflight_checks_test.go
Normal file
90
pkg/qliksense/preflight_checks_test.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package qliksense
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_initiateK8sOps(t *testing.T) {
|
||||
t.Skip()
|
||||
type args struct {
|
||||
opr string
|
||||
namespace string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid case",
|
||||
args: args{
|
||||
opr: fmt.Sprintf("version"),
|
||||
namespace: "ash-ns",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid case",
|
||||
args: args{
|
||||
opr: fmt.Sprintf("versions"),
|
||||
namespace: "ash-ns",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := initiateK8sOps(tt.args.opr, tt.args.namespace); (err != nil) != tt.wantErr {
|
||||
t.Errorf("initiateK8sOps() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_determinePlatformSpecificUrls(t *testing.T) {
|
||||
type args struct {
|
||||
platform string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
want1 string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid platform",
|
||||
args: args{
|
||||
platform: "windows",
|
||||
},
|
||||
want: fmt.Sprintf("%s%s", preflightBaseURL, preflightWindowsFile),
|
||||
want1: preflightWindowsFile,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid platform",
|
||||
args: args{
|
||||
platform: "unix",
|
||||
},
|
||||
want: "",
|
||||
want1: "",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, got1, err := determinePlatformSpecificUrls(tt.args.platform)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("determinePlatformSpecificUrls() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("determinePlatformSpecificUrls() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
if got1 != tt.want1 {
|
||||
t.Errorf("determinePlatformSpecificUrls() got1 = %v, want %v", got1, tt.want1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user