Compare commits

..

1 Commits

Author SHA1 Message Date
Sanat Nayar
e26ca77b05 linked to custom-cr 2020-02-04 14:57:12 -05:00
33 changed files with 162 additions and 2011 deletions

4
.gitignore vendored
View File

@@ -1,6 +1,2 @@
bin
.vscode
cmd/qliksense/__debug_bin
pkg/qliksense/crds
pkg/qliksense/packrd
pkg/qliksense/qliksense-packr.go

View File

@@ -13,7 +13,6 @@ VERSION ?= $(shell git describe --tags 2> /dev/null || echo v0)
PERMALINK ?= $(shell git describe --tags --exact-match &> /dev/null && echo latest || echo canary)
BUILDTAGS = netgo containers_image_ostree_stub exclude_graphdriver_devicemapper exclude_graphdriver_btrfs containers_image_openpgp
CLIENT_PLATFORM ?= $(shell go env GOOS)
CLIENT_ARCH ?= $(shell go env GOARCH)
RUNTIME_PLATFORM ?= linux
@@ -52,14 +51,12 @@ xbuild: $(BINDIR)/$(VERSION)/$(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH)$(FILE_EX
$(BINDIR)/$(VERSION)/$(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH)$(FILE_EXT):
mkdir -p $(dir $@)
GOOS=$(CLIENT_PLATFORM) GOARCH=$(CLIENT_ARCH) $(XBUILD) -o $@ ./cmd/$(MIXIN)
tar -czvf $(BINDIR)/$(VERSION)/$(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH).tar.gz -C $(BINDIR)/$(VERSION)/ $(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH)$(FILE_EXT)
#tar -C $(BINDIR)/$(VERSION)/ -cvf $(BINDIR)/$(VERSION)/$(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH).tar.gz $(MIXIN)-$(CLIENT_PLATFORM)-$(CLIENT_ARCH)$(FILE_EXT)
generate: get-crds packr2
go generate ./...
HAS_PACKR2 := $(shell packr2)
HAS_PACKR2 := $(shell command -v packr2)
packr2:
ifndef HAS_PACKR2
go get -u github.com/gobuffalo/packr/v2/packr2
@@ -72,12 +69,11 @@ clean: clean-packr
clean-packr: packr2
cd pkg/qliksense && packr2 clean
get-crds:
$(eval TMP := $(shell mktemp -d))
git clone git@github.com:qlik-oss/qliksense-operator.git -b ms-3 $(TMP)/operator
get-crds:
git clone git@github.com:qlik-oss/qliksense-operator.git -b custom_cr /tmp/operator
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
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

View File

@@ -1,31 +0,0 @@
# qliksense config
Config action will perform operations on configurations and contexts regarding the [qliksense-k8](https://github.com/qlik-oss/qliksense-k8s) release.
it will support following commands:
- `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
- `qliksense config set-context` - sets the context in which the Kubernetes cluster and resources live in
- `qliksense config set-secrets` - set secrets configurations into the qliksense context
- `qliksense config view` - view the qliksense operator CR
the global file that abstracts all the contexts is `config.yaml`, located at: `~/.qliksense/config.yaml`:
```yaml
apiVersion: config.qlik.com/v1
kind: QliksenseConfig
metadata:
name: QliksenseConfigMetadata
spec:
contexts:
- name: qlik-default
crFile: /Users/fff/.qliksense/contexts/qlik-default/qlik-default.yaml
- name: myqliksense
crFile: /Users/fff/.qliksense/contexts/myqliksense/myqliksense.yaml
- name: hello
crFile: /Users/fff/.qliksense/contexts/hello/hello.yaml
currentContext: hello
```

View File

@@ -3,11 +3,10 @@ package main
import (
"errors"
"fmt"
"strings"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
"strings"
)
type aboutCommandOptions struct {
@@ -45,7 +44,7 @@ qliksense about --profile=test
return err
} else if out, err := yaml.Marshal(vout); err != nil {
return err
} else if _, err := fmt.Println(string(out)); err != nil {
} else if _, err := fmt.Println(out); err != nil {
return err
}
return nil

View File

@@ -1,89 +0,0 @@
package main
import (
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
)
func setContextConfigCmd(q *qliksense.Qliksense) *cobra.Command {
var (
cmd *cobra.Command
)
cmd = &cobra.Command{
Use: "set-context",
Short: "Sets the context in which the Kubernetes cluster and resources live in",
Example: `qliksense config set-context <context_name>`,
RunE: func(cmd *cobra.Command, args []string) error {
return q.SetContextConfig(args)
},
}
return cmd
}
func listContextConfigCmd(q *qliksense.Qliksense) *cobra.Command {
var (
cmd *cobra.Command
)
cmd = &cobra.Command{
Use: "list-contexts",
Short: "retrieves the contexts and lists them",
Example: `qliksense config list-contexts`,
RunE: func(cmd *cobra.Command, args []string) error {
return q.ListContextConfigs()
},
}
return cmd
}
func setOtherConfigsCmd(q *qliksense.Qliksense) *cobra.Command {
var (
cmd *cobra.Command
)
cmd = &cobra.Command{
Use: "set",
Short: "configure a key value pair into the current context",
Example: `qliksense config set <key>=<value>`,
RunE: func(cmd *cobra.Command, args []string) error {
return q.SetOtherConfigs(args)
},
}
return cmd
}
func setConfigsCmd(q *qliksense.Qliksense) *cobra.Command {
var (
cmd *cobra.Command
)
cmd = &cobra.Command{
Use: "set-configs",
Short: "set configurations into the qliksense context",
Example: `qliksense config set-configs <key>=<value>`,
RunE: func(cmd *cobra.Command, args []string) error {
return q.SetConfigs(args)
},
}
return cmd
}
func setSecretsCmd(q *qliksense.Qliksense) *cobra.Command {
var (
cmd *cobra.Command
secret bool
)
cmd = &cobra.Command{
Use: "set-secrets",
Short: "set secrets configurations into the qliksense context",
Example: `qliksense config set-secrets <key>=<value> --secret=true`,
RunE: func(cmd *cobra.Command, args []string) error {
return q.SetSecrets(args, secret)
},
}
f := cmd.Flags()
f.BoolVar(&secret, "secret", false, "Whether secrets should be encrypted as a Kubernetes Secret resource")
return cmd
}

View File

@@ -2,6 +2,7 @@ package main
import (
"errors"
"github.com/Masterminds/semver/v3"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
)
@@ -16,10 +17,13 @@ func fetchCmd(q *qliksense.Qliksense) *cobra.Command {
if len(args) != 1 {
return errors.New("requires a version (i.e. v1.0.0)")
}
if _, err := semver.NewVersion(args[0]); err != nil {
return errors.New("is it not a valid version. should be something like this v1.0.0")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return q.FetchQK8s(args[0])
Run: func(cmd *cobra.Command, args []string) {
q.FetchQK8s(args[0])
},
}
return c

View File

@@ -2,12 +2,12 @@ package main
import (
"errors"
"github.com/Masterminds/semver/v3"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
)
func installCmd(q *qliksense.Qliksense) *cobra.Command {
opts := &qliksense.InstallCommandOptions{}
c := &cobra.Command{
Use: "install",
Short: "install a qliksense release",
@@ -17,18 +17,14 @@ func installCmd(q *qliksense.Qliksense) *cobra.Command {
if len(args) != 1 {
return errors.New("requires a version (i.e. v1.0.0)")
}
if _, err := semver.NewVersion(args[0]); err != nil {
return errors.New("is it not a valid version. should be something like this v1.0.0")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return q.InstallQK8s(args[0], opts)
return q.InstallQK8s(args[0])
},
}
f := c.Flags()
f.StringVarP(&opts.AcceptEULA, "acceptEULA", "a", "", "AcceptEULA for qliksense")
f.StringVarP(&opts.Namespace, "namespace", "n", "", "Namespace where to install the qliksense")
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")
return c
}

View File

@@ -2,20 +2,18 @@ package main
import (
"fmt"
ansi "github.com/mattn/go-colorable"
"github.com/mitchellh/go-homedir"
"github.com/qlik-oss/sense-installer/pkg"
"github.com/qlik-oss/sense-installer/pkg/api"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/ttacon/chalk"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/mitchellh/go-homedir"
"github.com/qlik-oss/sense-installer/pkg"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// To run this project in ddebug mode, run:
@@ -32,25 +30,16 @@ func initAndExecute() error {
qlikSenseHome string
err error
)
qlikSenseHome, err = setUpPaths()
if err != nil {
log.Fatal(err)
}
// create dirs and appropriate files for setting up contexts
api.LogDebugMessage("QliksenseHomeDir: %s", qlikSenseHome)
qliksenseClient, err := qliksense.New(qlikSenseHome)
if err != nil {
if err = rootCmd(qliksense.New(qlikSenseHome)).Execute(); err != nil {
return err
}
qliksenseClient.SetUpQliksenseDefaultContext()
cmd := rootCmd(qliksenseClient)
//levenstein checks
if levenstein(cmd) == false {
if err := cmd.Execute(); err != nil {
return err
}
}
return nil
}
@@ -69,11 +58,7 @@ func setUpPaths() (string, error) {
}
qlikSenseHome = filepath.Join(homeDir, qlikSenseDirVar)
}
if err := os.MkdirAll(qlikSenseHome, os.ModePerm); err != nil {
return "", err
}
os.Mkdir(qlikSenseHome, os.ModePerm)
return qlikSenseHome, nil
}
@@ -127,26 +112,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))
// add the set profile/namespace/storageClassName/git-repository config command as a sub-command to the app config command
configCmd.AddCommand(setOtherConfigsCmd(p))
// add the set ### config command as a sub-command to the app config sub-command
configCmd.AddCommand(setConfigsCmd(p))
// add the set ### config command as a sub-command to the app config sub-command
configCmd.AddCommand(setSecretsCmd(p))
// add the list config command as a sub-command to the app config sub-command
configCmd.AddCommand(listContextConfigCmd(p))
// add uninstall command
cmd.AddCommand(uninstallCmd(p))
return cmd
}
@@ -208,78 +173,3 @@ func copy(src, dst string) (int64, error) {
nBytes, err = io.Copy(destination, source)
return nBytes, err
}
func levenstein(cmd *cobra.Command) bool {
type closeCommand struct {
Name string
levensteinVal int
}
var lev closeCommand
lev.Name = ""
lev.levensteinVal = 10
if len(os.Args) > 1 {
args := os.Args[1]
for _, ctx := range cmd.Commands() {
val := *ctx
if args == val.Name() {
//found command
return false
} else {
currVal := comparator([]rune(args), []rune(val.Name()))
if currVal < lev.levensteinVal {
lev.levensteinVal = currVal
lev.Name = val.Name()
}
fmt.Println(currVal, val.Name())
}
}
if lev.levensteinVal <= 4 {
arg := []string{}
for _, cm := range os.Args {
arg = append(arg, cm)
}
arg[1] = lev.Name
out := ansi.NewColorableStdout()
fmt.Fprintln(out, chalk.Green.Color("Did you mean: "), chalk.Bold.TextStyle(strings.Join(arg, " ")), "?")
return true
}
}
return false
}
func comparator(str1, str2 []rune) int {
s1len := len(str1)
s2len := len(str2)
column := make([]int, len(str1)+1)
for y := 1; y <= s1len; y++ {
column[y] = y
}
for x := 1; x <= s2len; x++ {
column[0] = x
lastkey := x - 1
for y := 1; y <= s1len; y++ {
oldkey := column[y]
var incr int
if str1[y-1] != str2[x-1] {
incr = 1
}
column[y] = minimum(column[y]+1, column[y-1]+1, lastkey+incr)
lastkey = oldkey
}
}
return column[s1len]
}
func minimum(a, b, c int) int {
if a < b {
if a < c {
return a
}
} else {
if b < c {
return b
}
}
return c
}

View File

@@ -1,23 +0,0 @@
package main
import (
qapi "github.com/qlik-oss/sense-installer/pkg/api"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
)
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 + " ]",
Long: `Uninstall the deployed qliksense. By default uninstall the current context`,
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("")
},
}
return c
}

View File

@@ -1,20 +0,0 @@
package main
import (
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
)
func upgradeCmd(q *qliksense.Qliksense) *cobra.Command {
c := &cobra.Command{
Use: "upgrade",
Short: "upgrade qliksense release",
Long: `upgrade qliksesne release`,
Example: `qliksense upgrade <version>`,
RunE: func(cmd *cobra.Command, args []string) error {
return q.UpgradeQK8s()
},
}
return c
}

24
go.mod
View File

@@ -3,20 +3,26 @@ module github.com/qlik-oss/sense-installer
go 1.13
replace (
github.com/Sirupsen/logrus v1.0.5 => github.com/sirupsen/logrus v1.0.5
github.com/Sirupsen/logrus v1.3.0 => github.com/Sirupsen/logrus v1.0.6
github.com/Sirupsen/logrus v1.4.0 => github.com/sirupsen/logrus v1.0.6
// github.com/containerd/containerd v1.3.0-0.20190507210959-7c1e88399ec0 => github.com/containerd/containerd v1.3.2
github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309
// github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305
// golang.org/x/crypto v0.0.0-20190129210102-0709b304e793 => golang.org/x/crypto v0.0.0-20180904163835-0709b304e793
golang.org/x/sys => golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8
k8s.io/client-go => k8s.io/client-go v0.0.0-20191016111102-bec269661e48
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200206224201-2e697eccbad9
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200129153315-09eb26c762c8
)
require (
cloud.google.com/go v0.52.0 // indirect
cloud.google.com/go/storage v1.5.0 // indirect
github.com/Shopify/ejson v1.2.1
github.com/Masterminds/semver/v3 v3.0.3
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
github.com/aws/aws-sdk-go v1.28.9 // indirect
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 // indirect
@@ -31,13 +37,9 @@ require (
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7
github.com/docker/go v1.5.1-1 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/gobuffalo/envy v1.9.0 // indirect
github.com/gobuffalo/logger v1.0.3 // indirect
github.com/gobuffalo/packd v1.0.0 // indirect
github.com/gobuffalo/packr/v2 v2.7.1
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.3.3 // indirect
github.com/google/uuid v1.1.1
github.com/gorilla/mux v1.7.3 // indirect
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
@@ -50,24 +52,20 @@ require (
github.com/mitchellh/go-homedir v1.1.0
github.com/morikuni/aec v1.0.0 // indirect
github.com/pkg/errors v0.8.1
github.com/qlik-oss/k-apis v0.0.8
github.com/rogpeppe/go-internal v1.5.2 // indirect
github.com/qlik-oss/k-apis v0.0.2
github.com/spf13/cobra v0.0.5
github.com/spf13/viper v1.6.1
github.com/theupdateframework/notary v0.6.1 // indirect
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 // indirect
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 // indirect
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a // indirect
golang.org/x/net v0.0.0-20200202094626-16171245cfb2
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c // indirect
golang.org/x/tools v0.0.0-20200219054238-753a1d49df85 // indirect
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 // indirect
google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b // indirect
google.golang.org/grpc v1.27.0 // indirect
gopkg.in/dancannon/gorethink.v3 v3.0.5 // indirect
gopkg.in/fatih/pool.v2 v2.0.0 // indirect
gopkg.in/gorethink/gorethink.v3 v3.0.5 // indirect
gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v3 v3.0.0-20190924164351-c8b7dadae555
sigs.k8s.io/kustomize/api v0.3.2
vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787 // indirect
)

28
go.sum
View File

@@ -400,16 +400,10 @@ github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8=
github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
github.com/gobuffalo/envy v1.9.0 h1:eZR0DuEgVLfeIb1zIKt3bT4YovIMf9O9LXQeCZLXpqE=
github.com/gobuffalo/envy v1.9.0/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg=
github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc=
github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM=
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM=
github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI=
github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o=
github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
@@ -714,7 +708,6 @@ github.com/lib/pq v0.0.0-20180201184707-88edab080323/go.mod h1:5WUZQaWbwv1U+lTRe
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=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
@@ -907,10 +900,12 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/qlik-oss/k-apis v0.0.8 h1:8ZZefgFT+rI4PuE/sp3Wpc+d5LtZBKZuDb2G/YelTOs=
github.com/qlik-oss/k-apis v0.0.8/go.mod h1:KOFzKVIdRqp47ytnHg3+9zb8fTlnrQjO6aKiwcrCJUE=
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/qlik-oss/k-apis v0.0.1 h1:XgKSi2Lu/Jsc0XeBRCObw8urt9vVjtgr7INq6kjJpV0=
github.com/qlik-oss/k-apis v0.0.1/go.mod h1:FDHuThas5MNw192Sk2IGy9fv8S06BLiuz2vfskZ162Q=
github.com/qlik-oss/k-apis v0.0.2 h1:7Sdz7528of+34NijkhxSc964FTOl+RV3IMMJxiScmd0=
github.com/qlik-oss/k-apis v0.0.2/go.mod h1:KOFzKVIdRqp47ytnHg3+9zb8fTlnrQjO6aKiwcrCJUE=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200129153315-09eb26c762c8 h1:WLVkArXf58T+3SHDvSddE8P6OjkeeUtGEgHk8LDdfeo=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200129153315-09eb26c762c8/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=
@@ -923,8 +918,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY=
github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w=
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -947,7 +940,6 @@ github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lz
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
@@ -1116,11 +1108,8 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/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=
@@ -1153,7 +1142,6 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1278,8 +1266,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-20200219054238-753a1d49df85 h1:XNHaQ2CZDl/SjEZlUXGh7+OQvfLuFgmk3oNWkCFfERE=
golang.org/x/tools v0.0.0-20200219054238-753a1d49df85/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
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=

View File

@@ -3,12 +3,11 @@ package api
import (
"errors"
"fmt"
"github.com/jinzhu/copier"
"gopkg.in/yaml.v2"
"io/ioutil"
"os"
"path/filepath"
"github.com/jinzhu/copier"
"gopkg.in/yaml.v2"
)
// NewQConfig create QliksenseConfig object from file ~/.qliksense/config.yaml
@@ -25,14 +24,19 @@ func NewQConfig(qsHome string) *QliksenseConfig {
fmt.Println("yaml unmarshalling error ", err)
os.Exit(1)
}
qc.QliksenseHomePath = qsHome
return qc
}
// GetCR create a QliksenseCR object for a particular context
// from file ~/.qliksense/contexts/<contx-name>/<contx-name>.yaml
func (qc *QliksenseConfig) GetCR(contextName string) (*QliksenseCR, error) {
crFilePath := qc.getCRFilePath(contextName)
crFilePath := ""
for _, ctx := range qc.Spec.Contexts {
if ctx.Name == contextName {
crFilePath = ctx.CrFile
break
}
}
if crFilePath == "" {
return nil, errors.New("context name " + contextName + " not found")
}
@@ -77,83 +81,3 @@ func getCRObject(crfile string) (*QliksenseCR, error) {
}
return cr, nil
}
func (qc *QliksenseConfig) getCRFilePath(contextName string) string {
crFilePath := ""
for _, ctx := range qc.Spec.Contexts {
if ctx.Name == contextName {
crFilePath = ctx.CrFile
break
}
}
return crFilePath
}
func (qc *QliksenseConfig) IsRepoExist(contextName, version string) bool {
if _, err := os.Lstat(qc.BuildRepoPathForContext(contextName, version)); err != nil {
return false
}
return true
}
func (qc *QliksenseConfig) IsRepoExistForCurrent(version string) bool {
if _, err := os.Lstat(qc.BuildRepoPath(version)); err != nil {
return false
}
return true
}
func (qc *QliksenseConfig) BuildRepoPath(version string) string {
return qc.BuildRepoPathForContext(qc.Spec.CurrentContext, version)
}
func (qc *QliksenseConfig) BuildRepoPathForContext(contextName, version string) string {
return filepath.Join(qc.QliksenseHomePath, "contexts", contextName, "qlik-k8s", version)
}
func (qc *QliksenseConfig) BuildCurrentManifestsRoot(version string) string {
return filepath.Join(qc.BuildRepoPath(version), "manifests")
}
func (qc *QliksenseConfig) WriteCR(cr *QliksenseCR, contextName string) error {
crf := qc.getCRFilePath(contextName)
if crf == "" {
return errors.New("context name " + contextName + " not found")
}
by, err := yaml.Marshal(cr)
if err != nil {
fmt.Println("cannot marshal cr ", err)
return err
}
ioutil.WriteFile(crf, by, 0644)
return nil
}
func (qc *QliksenseConfig) WriteCurrentContextCR(cr *QliksenseCR) error {
return qc.WriteCR(cr, qc.Spec.CurrentContext)
}
func (qc *QliksenseConfig) IsContextExist(ctxName string) bool {
for _, ct := range qc.Spec.Contexts {
if ct.Name == ctxName {
return true
}
}
return false
}
func (cr *QliksenseCR) AddLabelToCr(key, value string) error {
if cr.Metadata.Labels == nil {
cr.Metadata.Labels = make(map[string]string)
}
cr.Metadata.Labels[key] = value
return nil
}
func (cr *QliksenseCR) GetString() (string, error) {
out, err := yaml.Marshal(cr)
if err != nil {
fmt.Println("cannot unmarshal cr ", err)
return "", err
}
return string(out), nil
}

View File

@@ -1,94 +0,0 @@
package api
import (
"fmt"
"io/ioutil"
"log"
"github.com/qlik-oss/k-apis/pkg/config"
"gopkg.in/yaml.v2"
)
const (
QliksenseConfigApiVersion = "config.qlik.com/v1"
QliksenseConfigKind = "QliksenseConfig"
QliksenseContextApiVersion = "qlik.com/v1"
QliksenseContextKind = "Qliksense"
QliksenseDefaultProfile = "docker-desktop"
DefaultRotateKeys = "yes"
QliksenseMetadataName = "QliksenseConfigMetadata"
)
// AddCommonConfig adds common configs into CRs
func (qliksenseCR *QliksenseCR) AddCommonConfig(contextName string) {
qliksenseCR.ApiVersion = QliksenseContextApiVersion
qliksenseCR.Kind = QliksenseContextKind
if qliksenseCR.Metadata == nil {
qliksenseCR.Metadata = &Metadata{}
}
if qliksenseCR.Metadata.Name == "" {
qliksenseCR.Metadata.Name = contextName
}
qliksenseCR.Spec = &config.CRSpec{}
qliksenseCR.Spec.Profile = QliksenseDefaultProfile
qliksenseCR.Spec.ReleaseName = contextName
qliksenseCR.Spec.RotateKeys = DefaultRotateKeys
}
// AddBaseQliksenseConfigs adds configs into config.yaml
func (qliksenseConfig *QliksenseConfig) AddBaseQliksenseConfigs(defaultQliksenseContext string) {
qliksenseConfig.ApiVersion = QliksenseConfigApiVersion
qliksenseConfig.Kind = QliksenseConfigKind
if qliksenseConfig.Metadata == nil {
qliksenseConfig.Metadata = &Metadata{}
}
qliksenseConfig.Metadata.Name = QliksenseMetadataName
if defaultQliksenseContext != "" {
if qliksenseConfig.Spec == nil {
qliksenseConfig.Spec = &ContextSpec{}
}
qliksenseConfig.Spec.CurrentContext = defaultQliksenseContext
}
}
// WriteToFile (content, targetFile) writes content into specified file
func WriteToFile(content interface{}, targetFile string) error {
if content == nil || targetFile == "" {
return nil
}
x, err := yaml.Marshal(content)
if err != nil {
err = fmt.Errorf("An error occurred during marshalling CR: %v", err)
log.Println(err)
return err
}
// Writing content
err = ioutil.WriteFile(targetFile, x, 0644)
if err != nil {
log.Println(err)
return err
}
LogDebugMessage("Wrote content into %s", targetFile)
return nil
}
// ReadFromFile (content, targetFile) reads content from specified sourcefile
func ReadFromFile(content interface{}, sourceFile string) error {
if content == nil || sourceFile == "" {
return nil
}
contents, err := ioutil.ReadFile(sourceFile)
if err != nil {
err = fmt.Errorf("There was an error reading from file: %s, %v", sourceFile, err)
log.Println(err)
return err
}
if err := yaml.Unmarshal(contents, content); err != nil {
err = fmt.Errorf("An error occurred during unmarshalling: %v", err)
log.Println(err)
return err
}
return nil
}

View File

@@ -1,94 +0,0 @@
package api
import (
"reflect"
"testing"
"github.com/qlik-oss/k-apis/pkg/config"
)
var (
testDir = "./tests"
)
func TestAddCommonConfig(t *testing.T) {
type args struct {
qliksenseCR *QliksenseCR
contextName string
}
tests := []struct {
name string
args args
want *QliksenseCR
}{
{
name: "valid case",
args: args{
qliksenseCR: &QliksenseCR{},
contextName: "myqliksense",
},
want: &QliksenseCR{
CommonConfig: CommonConfig{
ApiVersion: QliksenseContextApiVersion,
Kind: QliksenseContextKind,
Metadata: &Metadata{
Name: "myqliksense",
},
},
Spec: &config.CRSpec{
Profile: QliksenseDefaultProfile,
ReleaseName: "myqliksense",
RotateKeys: DefaultRotateKeys,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.args.qliksenseCR.AddCommonConfig(tt.args.contextName)
if !reflect.DeepEqual(tt.args.qliksenseCR, tt.want) {
t.Errorf("AddCommonConfig() = %+v, want %+v", tt.args.qliksenseCR, tt.want)
}
})
}
}
func TestAddBaseQliksenseConfigs(t *testing.T) {
type args struct {
qliksenseConfig *QliksenseConfig
defaultQliksenseContext string
}
tests := []struct {
name string
args args
want *QliksenseConfig
}{
{
name: "valid case",
args: args{
qliksenseConfig: &QliksenseConfig{},
defaultQliksenseContext: "qlik-default",
},
want: &QliksenseConfig{
CommonConfig: CommonConfig{
ApiVersion: QliksenseConfigApiVersion,
Kind: QliksenseConfigKind,
Metadata: &Metadata{
Name: QliksenseMetadataName,
},
},
Spec: &ContextSpec{
CurrentContext: "qlik-default",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.args.qliksenseConfig.AddBaseQliksenseConfigs(tt.args.defaultQliksenseContext)
if !reflect.DeepEqual(tt.args.qliksenseConfig, tt.want) {
t.Errorf("AddBaseQliksenseConfigs() = %+v, want %+v", tt.args.qliksenseConfig, tt.want)
}
})
}
}

View File

@@ -1,171 +0,0 @@
package api
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"log"
"path/filepath"
)
const (
RSA_KEY_LENGTH = 4096
privateKeyPath = "privKey"
publicKeyPath = "pubKey"
QliksensePublicKey = "qliksensePub"
QliksensePrivateKey = "qliksensePriv"
)
// GenerateAndStoreSecretKeypair generates and stores key pairs
func GenerateAndStoreSecretKeypair(secretsPath string) error {
LogDebugMessage("%s exists", secretsPath)
// creating contexts/qlik-default/secrets/qliksensePub and contexts/qlik-default/secrets/qliksensePriv files
publicKeyFilePath := filepath.Join(secretsPath, QliksensePublicKey)
privateKeyFilePath := filepath.Join(secretsPath, QliksensePrivateKey)
LogDebugMessage("Generating public-private key pair.....")
GenerateRSAEncryptionKeys(publicKeyFilePath, privateKeyFilePath)
LogDebugMessage("Generated public-private key pairs")
return nil
}
// GenerateRSAEncryptionKeys is used to generate a new public-private key pair
func GenerateRSAEncryptionKeys(publicKeyFilePath, privateKeyFilePath string) error {
LogDebugMessage("Generating new RSA key pair")
privateKey, err := rsa.GenerateKey(rand.Reader, RSA_KEY_LENGTH)
if err != nil {
log.Printf("error generating RSA private key: %v\n", err)
return err
}
privateKeyPEM := EncodePrivateKey(privateKey)
if err := writeContentToFile(privateKeyPEM, privateKeyFilePath); err != nil {
return err
}
pubKeyPEM, err2 := EncodePublicKey(&privateKey.PublicKey)
if err2 != nil {
log.Printf("error occurred when encoding public key: %v\n", err2)
return err2
}
if err := writeContentToFile(pubKeyPEM, publicKeyFilePath); err != nil {
return err
}
return nil
}
// writeContentToFile writes keys to a file
func writeContentToFile(keyData []byte, fileName string) error {
err := ioutil.WriteFile(fileName, keyData, 0600)
if err != nil {
log.Printf("error writing to file (%s): %v", fileName, err)
return err
}
LogDebugMessage("Key saved: %s", fileName)
return nil
}
// Encrypt encrypts data with public key
func Encrypt(pt []byte, pub *rsa.PublicKey) ([]byte, error) {
// hash := sha512.New()
// ciphertext, err := rsa.EncryptOAEP(hash, rand.Reader, pub, msg, nil)
ct, err := rsa.EncryptPKCS1v15(rand.Reader, pub, pt)
if err != nil {
log.Println(err)
return nil, err
}
return ct, nil
}
// Decrypt decrypts data with private key
func Decrypt(ct []byte, priv *rsa.PrivateKey) ([]byte, error) {
// hash := sha512.New()
// plaintext, err := rsa.DecryptOAEP(hash, rand.Reader, priv, ciphertext, nil)
pt, err := rsa.DecryptPKCS1v15(rand.Reader, priv, ct)
if err != nil {
log.Println(err)
return nil, err
}
return pt, nil
}
// EncodePrivateKey private key to bytes
func EncodePrivateKey(priv *rsa.PrivateKey) []byte {
privBytes := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(priv),
},
)
return privBytes
}
// EncodePublicKey public key to bytes
func EncodePublicKey(pub *rsa.PublicKey) ([]byte, error) {
pubASN1, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
log.Println(err)
return nil, err
}
pubBytes := pem.EncodeToMemory(&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubASN1,
})
return pubBytes, nil
}
// DecodeToPrivateKey bytes to private key
func DecodeToPrivateKey(priv []byte) (*rsa.PrivateKey, error) {
block, _ := pem.Decode(priv)
enc := x509.IsEncryptedPEMBlock(block)
b := block.Bytes
var err error
if enc {
log.Println("is encrypted pem block")
b, err = x509.DecryptPEMBlock(block, nil)
if err != nil {
log.Println(err)
return nil, err
}
}
key, err := x509.ParsePKCS1PrivateKey(b)
if err != nil {
log.Println(err)
return nil, err
}
return key, nil
}
// DecodeToPublicKey bytes to public key
func DecodeToPublicKey(pub []byte) (*rsa.PublicKey, error) {
block, _ := pem.Decode(pub)
enc := x509.IsEncryptedPEMBlock(block)
b := block.Bytes
var err error
if enc {
log.Println("is encrypted pem block")
b, err = x509.DecryptPEMBlock(block, nil)
if err != nil {
log.Println(err)
return nil, err
}
}
iface, err := x509.ParsePKIXPublicKey(b)
if err != nil {
log.Println(err)
return nil, err
}
key, ok := iface.(*rsa.PublicKey)
if !ok {
err := fmt.Errorf("Unable to decode public key")
log.Println(err)
return nil, err
}
return key, nil
}

View File

@@ -1,129 +0,0 @@
package api
import (
"encoding/base64"
"log"
"os"
"testing"
)
func Test_generateRSAEncryptionKeys(t *testing.T) {
tests := []struct {
name string
wantErr bool
}{
{
name: "valid case",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := GenerateAndStoreSecretKeypair(os.TempDir()); (err != nil) != tt.wantErr {
t.Errorf("generateRSAEncryptionKeys() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func Test_encryption_decryption(t *testing.T) {
privKeyBytes := []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIJJwIBAAKCAgEAwUCimKCidbF3UxEHPy8K+hvhklRB9JYhj5sJy0if4lTVibkK
1MrYCykOnmC40pPU9GLY1b8HxAg9tvyRn0YHUxOra6vVQaVcOVJhTM8D18d+lSr3
Lp1yiX+UGT4nzWI9+R1CCbwXrqeQVoZs6QZKynEXMkFI9/wNMOwPOvQFOSTuoEoC
O+zyTyUWEkNbUq825ELUQdIsjgmlWUOONudxsAr7ESRXW9QTHVh6uWmr3VRKZHby
1JdU3I/wjdlGg5M2dDuXy5nQO9w/nYLjJXiw+zzOetZ/+t7/VOkOpNTeJQhwTM1W
F7Y2VLetbi9FHgyzHatrduh07+XEiTbgDf3GIx2bp2p6oh0G3N2zpiLcK/aZj8ro
uWWydfFfsU3MZ4FfJDP8I6b9awxjmKYqIr6hiPQCJaLBED8mwK+I5evIbnKv6E6u
K+BApWA/R7ElragoFYbqQ1VpvntVMtJt9Dy5ZrI+IQARdXD3bb34oh0IPBhClnvv
MUc1cWxDoXEX6oJ4I+LzxE87Zkwnan9qOwengolMVKFwPx1o37qrbmrXID21kKt7
FL6xN4HxHLkItr1fKzdyWDFRHgASTAWfx5BIwvPuUW0vZHkvO80VyV2L63whVhPn
PASmFkbviomrBttYfpr2aGQqF/qR1Nlxe834MFxk1pS9LMa/WnzvFr0gWakCAwEA
AQKCAgARSp9B2N2wejibDiL/3E23I1eDqFZedDB8kPrHXbAwqDaTJCN79spt9TaB
pVXkQaYEV/Pe7EDdoX8kKGU/QxzUqiXkdHOYdBtUZbKfFMbbP9ZrsnR7j0r4UpoF
yDH3hprU93E5PcNAtW2M0GpeT1nR01yn+n908PCdOAIE3GC7RDq1zOl2QzVLL55R
9ATv2Q2oTvJ/ETc7XlGVMx4+e2cIwXLFjeLjLI6pSYlxnarrGuetJZeEviWxto9n
odFVZI6yx8JFTXX8ZTCr/1IjwDDVyhMPmrHI2Lsv9cqBpSpbVe32cUkKxhsGaYjz
GvesQKamOPhco2ATNxPm0yopFlPsGKMfVl0BK0J6BqFh1BvU/SYJmXfnFuUNO3vV
4u2Saa0q1iddxV0rXDwIqUfn+S6rwzK0G7y8bH2yvpB2VwiG3TFPnULep4wsefNq
Fj92kqFBjacGpQLEEslUY0CMgeZ2+NuBQSUTscP3wBRsottMR6YXJtINdvfHBx+e
EcN71z8D00w3mYqIQ7qb4Ml6HOqknunn58g43L9sACMUMTlEBXa9pUnScNYgWBAz
W2q2mH37cIydM2JRZPpA8B4yTHt5ugJmChwyNFM7941arjKrebH+6AzLkofGedOP
zg+vZQuPEXWs+3MBBnkWoyJW3Y0fbQdjsuQTtnd+7iyoxoBroQKCAQEA4dIiFlIS
MDfRhQQWSiDvaw9aneDEJ3uo63ZRH5tm/IynLgtjYgEm/ZxlBCQgqRKLYELBxhu8
SaF0uPK8pmpFJt0mIwSlsdeVhuE2obQeKUCczaqrKeaHS3PdWLjTlwph81BGRkHy
qfqtNylyyMxrdEbnR51EtsWgFq6anTUAui1Q09JMuMNZRMOzDs1F4gExgD22rc0V
c9YQ+jHJRxBGtNKMpMEqc8cvaxBidbItrN9SMTSWog7uYPBuEuaJ6K9vpgyJMOzJ
SYcQEFGqgIqIDCg+ABE4d/4YROMKZ1DV/bJCind9brUHSx6XALsF0nC5c1Q9TnUL
qI2khOwts4KYKwKCAQEA2xRC6Az97Vkdzu7BjLJ1FKmx4S2nEEgVS12ds82U+5Xf
BHKAJnjqlqmmpzzJG+d77IYktz0+mey1QCNkqlm2fhuKs8LZMnpZRf0l8VcoBsUP
/xKz7wfiE7RRFZtLJhPp4hhe43GzX5/JFMWMnC6UykwQbj4t1E/GNM/Suqwvg12M
wktAJ6nqLgfhjQSO4xWo+nPzcbX+fNtrPCZVrBhYXihhcwRRNImWUCGJ6J4LMdPY
Y9Z59qhOvE9cReH/Xw1av46omyiSyAqlgPyZ/kzA2IJSqYCjiQR/2+RD/g13jpcJ
jatXLVZ8MJSL5OTS40G/HHTNNpNHbKKh0GOyxBA3ewKCAQBAn8UXhCcmW2L/YPsL
/b7mcX9qPP+FmRLvR23R0MQ5M/tH5wRq8I969n3GIJykJeVzB8eybQ+GNslTgEvS
iAkAJTubu+G7MkndTqg2wHf9MDtvdA8Fr646Po8yq7oJuHPtkKR7yLWsRUu6xIbP
xgheP0hCq1QVxhqZQyCGKrvpi7xc0gsYuPbcAfFFJCOCmPrUi1SzCkTAYJt9LjA+
wP6rErIjGBCRD4iXaBn1OqdtmH9KC5WsDP/VCBlIGWeQCly2NVIxiSHVg+xp7yUP
IhXq/L05gbQaSsIhPKQmivCiaJg4The8TdwneDqYf+0bmxzHT203/bD3bImPbJNr
ksz/AoIBAEwu4Y1cZzkQUmNRd5D7xecnk6ngfEYXKwCIT3zlMrfCSEl9n77BMaKu
4Dsr0iuX9eosQ7xM2eYhAG6LYEg05lc4MKWOToVVMpI6E+W3Dz47bPKgiF3I+f8s
Jz5CQIG/TwfGvciOE3hfUkec4ua09BzdEqGjkcBQ9XYMBxXPJr6h2379OBQS7FKR
fwfQ2/dv4tElXTTfut2kV8gU9Jnh5Wjo1epvR+XjKpg28YQo4W+0YX1magcyRB8L
4eSTUIC3XiVa8Jr0IwbZXPBb5xkdi7o+p4w2JahSHjxTRqmj+T1mnHXdbXVgq9Mg
9Pzl7cgFZvX4UBx4XtASRf73jITNtt0CggEADH9K+O7FrIOSQly0sMvsRCMtejp3
o+MDh1Q+vEg2kEgNXjS4ZFVljUpM2kg1OdUz7feS4dLXUJiIQ8ZWtZPedcq7wjHd
02he5+s06l0jPifN3tX1ADfXGpXg5R2fbkrIzakkPP5/RO/aDxIUo7qhklNsVTXO
VlGGfWLdk0ekA4upKm02Q1+YOlbIcAicEYYY8K7IffUwnohzKwL9yfuGi1VKTXpE
4fzdegsHI03FSqR7V+LvtBpIupQ7RO4kuBmCEyI4E9FVknchg4te4gO3qwd9y0rJ
Gu7HNIOrwOHzviI7J6Nd/l9MmeKqklHSgJvko/f5TmiXuQQ8xDZf84rcjQ==
-----END RSA PRIVATE KEY-----
`)
publicKeyBytes := []byte(`-----BEGIN RSA PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwUCimKCidbF3UxEHPy8K
+hvhklRB9JYhj5sJy0if4lTVibkK1MrYCykOnmC40pPU9GLY1b8HxAg9tvyRn0YH
UxOra6vVQaVcOVJhTM8D18d+lSr3Lp1yiX+UGT4nzWI9+R1CCbwXrqeQVoZs6QZK
ynEXMkFI9/wNMOwPOvQFOSTuoEoCO+zyTyUWEkNbUq825ELUQdIsjgmlWUOONudx
sAr7ESRXW9QTHVh6uWmr3VRKZHby1JdU3I/wjdlGg5M2dDuXy5nQO9w/nYLjJXiw
+zzOetZ/+t7/VOkOpNTeJQhwTM1WF7Y2VLetbi9FHgyzHatrduh07+XEiTbgDf3G
Ix2bp2p6oh0G3N2zpiLcK/aZj8rouWWydfFfsU3MZ4FfJDP8I6b9awxjmKYqIr6h
iPQCJaLBED8mwK+I5evIbnKv6E6uK+BApWA/R7ElragoFYbqQ1VpvntVMtJt9Dy5
ZrI+IQARdXD3bb34oh0IPBhClnvvMUc1cWxDoXEX6oJ4I+LzxE87Zkwnan9qOwen
golMVKFwPx1o37qrbmrXID21kKt7FL6xN4HxHLkItr1fKzdyWDFRHgASTAWfx5BI
wvPuUW0vZHkvO80VyV2L63whVhPnPASmFkbviomrBttYfpr2aGQqF/qR1Nlxe834
MFxk1pS9LMa/WnzvFr0gWakCAwEAAQ==
-----END RSA PUBLIC KEY-----
`)
origStr := "Value1234"
pubKey, err := DecodeToPublicKey(publicKeyBytes)
if err != nil {
t.Error(err)
t.FailNow()
}
privKey, err := DecodeToPrivateKey(privKeyBytes)
if err != nil {
t.Error(err)
t.FailNow()
}
encData, err := Encrypt([]byte(origStr), pubKey)
if err != nil {
t.Error(err)
t.FailNow()
}
encDataStr := base64.StdEncoding.EncodeToString(encData)
log.Printf("encrypted data: %s\n", encDataStr)
dec, _ := base64.StdEncoding.DecodeString(encDataStr)
data, err := Decrypt(dec, privKey)
if err != nil {
t.Error(err)
t.FailNow()
}
if string(data) != origStr {
t.Error("original string and decrypted string don't match")
t.FailNow()
}
log.Printf("decrypted data: %s\n", data)
}

View File

@@ -8,14 +8,6 @@ import (
)
func KubectlApply(manifests string) error {
return kubectlOperation(manifests, "apply")
}
func KubectlDelete(manifests string) error {
return kubectlOperation(manifests, "delete")
}
func kubectlOperation(manifests string, oprName string) error {
tempYaml, err := ioutil.TempFile("", "")
if err != nil {
fmt.Println("cannot create file ", err)
@@ -23,13 +15,7 @@ func kubectlOperation(manifests string, oprName string) error {
}
tempYaml.WriteString(manifests)
var cmd *exec.Cmd
if oprName == "apply" {
cmd = exec.Command("kubectl", oprName, "-f", tempYaml.Name(), "--validate=false")
} else {
cmd = exec.Command("kubectl", oprName, "-f", tempYaml.Name())
}
cmd := exec.Command("kubectl", "apply", "-f", tempYaml.Name(), "--validate=false")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()

View File

@@ -11,9 +11,8 @@ type CommonConfig struct {
// QliksenseConfig is exported
type QliksenseConfig struct {
CommonConfig `json:",inline" yaml:",inline"`
Spec *ContextSpec `json:"spec" yaml:"spec"`
QliksenseHomePath string `json:"-" yaml:"-"`
CommonConfig `json:",inline" yaml:",inline"`
Spec *ContextSpec `json:"spec" yaml:"spec"`
}
// QliksenseCR is exported
@@ -39,10 +38,3 @@ type Metadata struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
}
// ServiceKeyValue holds the combination of service, key and value
type ServiceKeyValue struct {
SvcName string
Key string
Value string
}

View File

@@ -1,88 +0,0 @@
package api
import (
"fmt"
"io/ioutil"
"log"
"os"
"regexp"
)
func checkExists(filename string, isFile bool) os.FileInfo {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
if isFile {
LogDebugMessage("File does not exist")
} else {
LogDebugMessage("Dir does not exist")
}
return nil
}
LogDebugMessage("File exists")
return info
}
// FileExists checks if a file exists
func FileExists(filename string) bool {
if fe := checkExists(filename, true); fe != nil && !fe.IsDir() {
return true
}
return false
}
// DirExists checks if a directory exists
func DirExists(dirname string) bool {
if fe := checkExists(dirname, false); fe != nil && fe.IsDir() {
return true
}
return false
}
// LogDebugMessage logs a debug message
func LogDebugMessage(strMessage string, args ...interface{}) {
if os.Getenv("QLIKSENSE_DEBUG") == "true" {
log.Printf(strMessage, args...)
}
}
// ReadKeys reads key file from disk
func ReadKeys(keyFile string) ([]byte, error) {
keybyteArray, err := ioutil.ReadFile(keyFile)
if err != nil {
err = fmt.Errorf("There was an error reading from file: %s, %v", keyFile, err)
log.Println(err)
return nil, err
} else {
LogDebugMessage("Read key as byte[]: %+v", keybyteArray)
}
return keybyteArray, nil
}
// ProcessConfigArgs processes args and returns an service, key, value slice
func ProcessConfigArgs(args []string) ([]*ServiceKeyValue, error) {
// prepare received args
// split args[0] into key and value
if len(args) == 0 {
err := fmt.Errorf("No args were provided. Please provide args to configure the current context")
log.Println(err)
return nil, err
}
resultSvcKV := make([]*ServiceKeyValue, len(args))
re1 := regexp.MustCompile(`(\w{1,})\[name=(\w{1,})\]=("*[\w\-_/:0-9]+"*)`)
for i, arg := range args {
LogDebugMessage("Arg received: %s", arg)
result := re1.FindStringSubmatch(arg)
// check if result array's length is == 4 (index 0 - is the full match & indices 1,2,3- are the fields we need)
if len(result) != 4 {
err := fmt.Errorf("Please provide valid args for this command")
log.Println(err)
return nil, err
}
resultSvcKV[i] = &ServiceKeyValue{
SvcName: result[1],
Key: result[2],
Value: result[3],
}
}
return resultSvcKV, nil
}

View File

@@ -2,7 +2,6 @@ package qliksense
import (
"bytes"
"errors"
"io"
"io/ioutil"
"os"
@@ -12,7 +11,6 @@ import (
"sort"
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
"gopkg.in/yaml.v2"
)
@@ -63,8 +61,8 @@ const (
defaultGitUrl = "https://github.com/qlik-oss/qliksense-k8s"
)
func (q *Qliksense) About(gitRef, profile string) (*VersionOutput, error) {
configDirectory, isTemporary, profile, err := q.getConfigDirectory(defaultGitUrl, gitRef, profile)
func (p *Qliksense) About(gitRef, profile string) (*VersionOutput, error) {
configDirectory, isTemporary, profile, err := getConfigDirectory(defaultGitUrl, gitRef, profile)
if err != nil {
return nil, err
}
@@ -72,10 +70,6 @@ func (q *Qliksense) About(gitRef, profile string) (*VersionOutput, error) {
defer os.RemoveAll(configDirectory)
}
return q.AboutDir(configDirectory, profile)
}
func (q *Qliksense) AboutDir(configDirectory, profile string) (*VersionOutput, error) {
chartVersion, err := getChartVersion(filepath.Join(configDirectory, "transformers", "qseokversion.yaml"), "qliksense")
if err != nil {
return nil, err
@@ -97,7 +91,7 @@ func (q *Qliksense) AboutDir(configDirectory, profile string) (*VersionOutput, e
}, nil
}
func (q *Qliksense) getConfigDirectory(gitUrl, gitRef, profileEntered string) (dir string, isTemporary bool, profile string, err error) {
func getConfigDirectory(gitUrl, gitRef, profileEntered string) (dir string, isTemporary bool, profile string, err error) {
profile = profileEntered
if profile == "" {
profile = defaultProfile
@@ -119,13 +113,13 @@ func (q *Qliksense) getConfigDirectory(gitUrl, gitRef, profileEntered string) (d
return dir, false, profile, nil
}
var profileFromCurrentContext string
exists, dir, profileFromCurrentContext, err = q.ConfigExistsInCurrentContext()
var profileFromCR string
exists, dir, profileFromCR, err = configExistsInCR()
if err != nil {
return "", false, "", err
} else if exists {
if profileEntered == "" {
profile = profileFromCurrentContext
profile = profileFromCR
}
return dir, false, profile, nil
}
@@ -170,17 +164,8 @@ func configExistsInCurrentDirectory(profile string) (exists bool, currentDirecto
return exists, currentDirectory, err
}
func (q *Qliksense) ConfigExistsInCurrentContext() (exists bool, directory string, profile string, err error) {
qConfig := qapi.NewQConfig(q.QliksenseHome)
if currentCr, err := qConfig.GetCurrentCR(); err != nil {
return false, "", "", err
} else if currentCr.Spec.ManifestsRoot == "" {
return false, "", "", nil
} else if path.Base(currentCr.Spec.ManifestsRoot) != "manifests" {
return false, "", "", errors.New("currentCr.Spec.ManifestsRoot path should terminate with manifests/")
} else {
return true, path.Join(currentCr.Spec.ManifestsRoot, "../"), currentCr.Spec.Profile, nil
}
func configExistsInCR() (exists bool, directory string, profile string, err error) {
return exists, directory, profile, err
}
func getImageList(yamlContent []byte) ([]string, error) {

View File

@@ -2,13 +2,10 @@ package qliksense
import (
"fmt"
"io/ioutil"
"os"
"path"
"reflect"
"testing"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
)
func Test_About_getImageList(t *testing.T) {
@@ -263,13 +260,13 @@ func Test_About_getConfigDirectory(t *testing.T) {
var testCases = []struct {
name string
setup func(t *testing.T) (q *Qliksense, gitUrl, gitRef, profileEntered string)
verify func(q *Qliksense, configDir string, isTemporary bool, profile string) (ok bool, reason string, err error)
cleanup func(q *Qliksense, configDir string) error
setup func(t *testing.T) (gitUrl, gitRef, profileEntered string)
verify func(configDir string, isTemporary bool, profile string) (ok bool, reason string, err error)
cleanup func(configDir string) error
}{
{
name: "config in current directory and default profile",
setup: func(t *testing.T) (q *Qliksense, gitUrl, gitRef, profileEntered string) {
setup: func(t *testing.T) (gitUrl, gitRef, profileEntered string) {
currentDirectory, err := os.Getwd()
if err != nil {
t.Fatalf("error obtaining current directory: %v\n", err)
@@ -280,9 +277,9 @@ func Test_About_getConfigDirectory(t *testing.T) {
if err != nil {
t.Fatalf("error making path: %v, err: %v\n", defaultProfilePath, err)
}
return &Qliksense{}, "no-clone-for-you", "", ""
return "no-clone-for-you", "", ""
},
verify: func(_ *Qliksense, configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
verify: func(configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
currentDirectory, err := os.Getwd()
if err != nil {
return false, "", err
@@ -302,7 +299,7 @@ func Test_About_getConfigDirectory(t *testing.T) {
return true, "", nil
},
cleanup: func(_ *Qliksense, configDir string) error {
cleanup: func(configDir string) error {
if currentDirectory, err := os.Getwd(); err != nil {
return err
} else if err := os.RemoveAll(path.Join(currentDirectory, "manifests")); err != nil {
@@ -313,7 +310,7 @@ func Test_About_getConfigDirectory(t *testing.T) {
},
{
name: "config in current directory and profile specified",
setup: func(t *testing.T) (q *Qliksense, gitUrl, gitRef, profileEntered string) {
setup: func(t *testing.T) (gitUrl, gitRef, profileEntered string) {
currentDirectory, err := os.Getwd()
if err != nil {
t.Fatalf("error obtaining current directory: %v\n", err)
@@ -325,9 +322,9 @@ func Test_About_getConfigDirectory(t *testing.T) {
if err != nil {
t.Fatalf("error making path: %v, err: %v\n", defaultProfilePath, err)
}
return &Qliksense{}, "no-clone-for-you", "", profileEntered
return "no-clone-for-you", "", profileEntered
},
verify: func(_ *Qliksense, configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
verify: func(configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
currentDirectory, err := os.Getwd()
if err != nil {
return false, "", err
@@ -347,7 +344,7 @@ func Test_About_getConfigDirectory(t *testing.T) {
return true, "", nil
},
cleanup: func(_ *Qliksense, configDir string) error {
cleanup: func(configDir string) error {
if currentDirectory, err := os.Getwd(); err != nil {
return err
} else if err := os.RemoveAll(path.Join(currentDirectory, "manifests")); err != nil {
@@ -358,10 +355,10 @@ func Test_About_getConfigDirectory(t *testing.T) {
},
{
name: "config downloaded from git based on specific git ref and default profile used",
setup: func(t *testing.T) (q *Qliksense, gitUrl, gitRef, profileEntered string) {
return &Qliksense{}, "https://github.com/test/HelloWorld", "asd", ""
setup: func(t *testing.T) (gitUrl, gitRef, profileEntered string) {
return "https://github.com/test/HelloWorld", "asd", ""
},
verify: func(_ *Qliksense, configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
verify: func(configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
ok, reason = verifyAsdBranch(configDir)
if !ok {
return ok, reason, nil
@@ -377,7 +374,7 @@ func Test_About_getConfigDirectory(t *testing.T) {
return true, "", nil
},
cleanup: func(_ *Qliksense, configDir string) error {
cleanup: func(configDir string) error {
tmpDir := os.TempDir()
if path.Clean(path.Dir(path.Dir(configDir))) == path.Clean(tmpDir) && path.Base(configDir) == "repo" {
@@ -391,10 +388,10 @@ func Test_About_getConfigDirectory(t *testing.T) {
},
{
name: "config downloaded from git based on specific git ref and profile specified",
setup: func(t *testing.T) (q *Qliksense, gitUrl, gitRef, profileEntered string) {
return &Qliksense{}, "https://github.com/test/HelloWorld", "asd", "foo"
setup: func(t *testing.T) (gitUrl, gitRef, profileEntered string) {
return "https://github.com/test/HelloWorld", "asd", "foo"
},
verify: func(_ *Qliksense, configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
verify: func(configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
ok, reason = verifyAsdBranch(configDir)
if !ok {
return ok, reason, nil
@@ -410,7 +407,7 @@ func Test_About_getConfigDirectory(t *testing.T) {
return true, "", nil
},
cleanup: func(_ *Qliksense, configDir string) error {
cleanup: func(configDir string) error {
tmpDir := os.TempDir()
if path.Clean(path.Dir(path.Dir(configDir))) == path.Clean(tmpDir) && path.Base(configDir) == "repo" {
@@ -424,21 +421,10 @@ func Test_About_getConfigDirectory(t *testing.T) {
},
{
name: "config downloaded from git from master branch and default profile used",
setup: func(t *testing.T) (q *Qliksense, gitUrl, gitRef, profileEntered string) {
if qliksenseHome, err := ioutil.TempDir("", ""); err != nil {
t.Fatalf("error creating tmp qliksenseHome directory: %v\n", err)
return nil, "", "", ""
} else {
q := &Qliksense{QliksenseHome: qliksenseHome}
if err := q.SetUpQliksenseDefaultContext(); err != nil {
t.Fatalf("error setting up default context in the tmp dir: %v\n", err)
return nil, "", "", ""
} else {
return q, "https://github.com/test/HelloWorld", "", ""
}
}
setup: func(t *testing.T) (gitUrl, gitRef, profileEntered string) {
return "https://github.com/test/HelloWorld", "", ""
},
verify: func(_ *Qliksense, configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
verify: func(configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
ok, reason = verifyMasterBranch(configDir)
if !ok {
return ok, reason, nil
@@ -454,37 +440,24 @@ func Test_About_getConfigDirectory(t *testing.T) {
return true, "", nil
},
cleanup: func(q *Qliksense, configDir string) error {
cleanup: func(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)
if err := os.RemoveAll(tmpTmpDir); err != nil {
return err
}
}
if err := os.RemoveAll(q.QliksenseHome); err != nil {
return err
}
return nil
},
},
{
name: "config downloaded from git from master branch and profile specified",
setup: func(t *testing.T) (q *Qliksense, gitUrl, gitRef, profileEntered string) {
if qliksenseHome, err := ioutil.TempDir("", ""); err != nil {
t.Fatalf("error creating tmp qliksenseHome directory: %v\n", err)
return nil, "", "", ""
} else {
q := &Qliksense{QliksenseHome: qliksenseHome}
if err := q.SetUpQliksenseDefaultContext(); err != nil {
t.Fatalf("error setting up default context in the tmp dir: %v\n", err)
return nil, "", "", ""
} else {
return q, "https://github.com/test/HelloWorld", "", "foo"
}
}
setup: func(t *testing.T) (gitUrl, gitRef, profileEntered string) {
return "https://github.com/test/HelloWorld", "", "foo"
},
verify: func(_ *Qliksense, configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
verify: func(configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
ok, reason = verifyMasterBranch(configDir)
if !ok {
return ok, reason, nil
@@ -500,78 +473,31 @@ func Test_About_getConfigDirectory(t *testing.T) {
return true, "", nil
},
cleanup: func(q *Qliksense, configDir string) error {
cleanup: func(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)
if err := os.RemoveAll(tmpTmpDir); err != nil {
return err
}
}
if err := os.RemoveAll(q.QliksenseHome); err != nil {
return err
}
return nil
},
},
{
name: "config loaded from current context",
setup: func(t *testing.T) (q *Qliksense, gitUrl, gitRef, profileEntered string) {
if qliksenseHome, err := ioutil.TempDir("", ""); err != nil {
t.Fatalf("error creating tmp qliksenseHome directory: %v\n", err)
return nil, "", "", ""
} else {
q := &Qliksense{QliksenseHome: qliksenseHome}
if err := q.SetUpQliksenseDefaultContext(); err != nil {
t.Fatalf("error setting up default context in the tmp dir: %v\n", err)
return nil, "", "", ""
} else if err := q.FetchQK8s("master"); err != nil {
t.Fatalf("error fetching master config to the tmp dir: %v\n", err)
return nil, "", "", ""
} else {
return q, "no-git-clone-for-you", "", ""
}
}
},
verify: func(q *Qliksense, configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
qConfig := qapi.NewQConfig(q.QliksenseHome)
expectedConfigDir := qConfig.BuildRepoPath("master")
if configDir != expectedConfigDir {
return false, fmt.Sprintf("expected configDir to be %v", expectedConfigDir), nil
}
if isTemporary {
return false, "expected isTemporary to be false", nil
}
if profile != "docker-desktop" {
return false, fmt.Sprintf("expected profile to be: docker-desktop, but it was: %v", profile), nil
}
return true, "", nil
},
cleanup: func(q *Qliksense, configDir string) error {
if err := os.RemoveAll(q.QliksenseHome); err != nil {
return err
}
return nil
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
q, gitUrl, gitRef, profileEntered := testCase.setup(t)
configDirectory, isTemporary, profile, err := q.getConfigDirectory(gitUrl, gitRef, profileEntered)
configDirectory, isTemporary, profile, err := getConfigDirectory(testCase.setup(t))
if err != nil {
t.Fatalf("unexpected error: %v\n", err)
}
if ok, reason, err := testCase.verify(q, configDirectory, isTemporary, profile); err != nil {
if ok, reason, err := testCase.verify(configDirectory, isTemporary, profile); err != nil {
t.Fatalf("unexpected verification error: %v\n", err)
} else if !ok {
t.Fatalf("verification failed: %v\n", reason)
} else if err := testCase.cleanup(q, configDirectory); err != nil {
} else if err := testCase.cleanup(configDirectory); err != nil {
t.Fatalf("unexpected cleanup error: %v\n", err)
}
})

View File

@@ -2,14 +2,11 @@ package qliksense
import (
"fmt"
"os"
"path"
"path/filepath"
"github.com/mitchellh/go-homedir"
"github.com/qlik-oss/k-apis/pkg/cr"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
"gopkg.in/yaml.v2"
)
const (
@@ -25,43 +22,25 @@ func (q *Qliksense) ConfigApplyQK8s() error {
fmt.Println("cannot get the current-context cr", err)
return err
}
return q.applyConfigToK8s(qcr, "")
return applyConfigToK8s(qcr)
}
func (q *Qliksense) applyConfigToK8s(qcr *qapi.QliksenseCR, cmd string) error {
func applyConfigToK8s(qcr *qapi.QliksenseCR) error {
// apply qliksense-init crd first
mroot := qcr.Spec.GetManifestsRoot()
qInitMsPath := filepath.Join(mroot, Q_INIT_CRD_PATH)
if qcr.Spec.RotateKeys != "None" {
if err := os.Unsetenv("EJSON_KEY"); err != nil {
fmt.Printf("error unsetting EJSON_KEY environment variable: %v\n", err)
return err
}
if err := os.Setenv("EJSON_KEYDIR", q.QliksenseEjsonKeyDir); err != nil {
fmt.Printf("error setting EJSON_KEYDIR environment variable: %v\n", err)
return err
}
}
if cmd != "upgrade" {
qInitByte, err := executeKustomizeBuild(qInitMsPath)
if err != nil {
fmt.Println("cannot generate crds for qliksense-init", err)
return err
}
if err = qapi.KubectlApply(string(qInitByte)); err != nil {
return err
}
}
userHomeDir, err := homedir.Dir()
qInitByte, err := executeKustomizeBuild(qInitMsPath)
if err != nil {
fmt.Printf(`error fetching user's home directory: %v\n`, err)
fmt.Println("cannot generate crds for qliksense-init", err)
return err
}
if err = qapi.KubectlApply(string(qInitByte)); err != nil {
return err
}
// generate patches
cr.GeneratePatches(qcr.Spec, path.Join(userHomeDir, ".kube", "config"))
cr.GeneratePatches(qcr.Spec)
// apply generated manifests
profilePath := filepath.Join(qcr.Spec.ManifestsRoot, qcr.Spec.Profile)
mByte, err := executeKustomizeBuild(profilePath)
@@ -89,15 +68,15 @@ func (q *Qliksense) ConfigViewCR() error {
func (q *Qliksense) getCurrentCRString() (string, error) {
qConfig := qapi.NewQConfig(q.QliksenseHome)
return q.getCRString(qConfig.Spec.CurrentContext)
}
func (q *Qliksense) getCRString(contextName string) (string, error) {
qConfig := qapi.NewQConfig(q.QliksenseHome)
qcr, err := qConfig.GetCR(contextName)
qcr, err := qConfig.GetCurrentCR()
if err != nil {
fmt.Println("cannot get the context cr", err)
fmt.Println("cannot get the current-context cr", err)
return "", err
}
return qcr.GetString()
out, err := yaml.Marshal(qcr)
if err != nil {
fmt.Println("cannot unmarshal cr ", err)
return "", err
}
return string(out), nil
}

View File

@@ -1,355 +0,0 @@
package qliksense
import (
"crypto/rsa"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"text/tabwriter"
b64 "encoding/base64"
ansi "github.com/mattn/go-colorable"
"github.com/qlik-oss/sense-installer/pkg/api"
"github.com/ttacon/chalk"
)
const (
// Below are some constants to support qliksense context setup
QliksenseConfigHome = "/.qliksense"
QliksenseConfigContextHome = "/.qliksense/contexts"
QliksenseConfigFile = "config.yaml"
QliksenseContextsDir = "contexts"
DefaultQliksenseContext = "qlik-default"
MaxContextNameLength = 17
QliksenseSecretsDir = "secrets"
)
// SetSecrets - set-secrets <key>=<value> commands
func (q *Qliksense) SetSecrets(args []string, isK8sSecret bool) error {
api.LogDebugMessage("Args received: %v\n", args)
api.LogDebugMessage("isK8sSecret: %v\n", isK8sSecret)
// retieve current context from config.yaml
qliksenseCR, qliksenseContextsFile, err := retrieveCurrentContextInfo(q)
if err != nil {
return err
}
secretKeyPairLocation := filepath.Join(q.QliksenseHome, QliksenseSecretsDir, QliksenseContextsDir, qliksenseCR.Metadata.Name, QliksenseSecretsDir)
api.LogDebugMessage("SecretKeyLocation to store key pair: %s", secretKeyPairLocation)
if os.Getenv("QLIKSENSE_KEY_LOCATION") != "" {
api.LogDebugMessage("Env variable: QLIKSENSE_KEY_LOCATION= %s", os.Getenv("QLIKSENSE_KEY_LOCATION"))
secretKeyPairLocation = os.Getenv("QLIKSENSE_KEY_LOCATION")
}
// Env var: QLIKSENSE_KEY_LOCATION hasn't been set, so dropping key pair in the location:
// /.qliksense/secrets/contexts/<current-context>/secrets/
api.LogDebugMessage("Using default location to store keys: %s", secretKeyPairLocation)
publicKeyFilePath := filepath.Join(secretKeyPairLocation, api.QliksensePublicKey)
privateKeyFilePath := filepath.Join(secretKeyPairLocation, api.QliksensePrivateKey)
// try to create the dir if it doesn't exist
if !api.FileExists(publicKeyFilePath) || !api.FileExists(privateKeyFilePath) {
api.LogDebugMessage("Qliksense secretKeyLocation dir does not exist, creating it now: %s", secretKeyPairLocation)
if err := os.MkdirAll(secretKeyPairLocation, os.ModePerm); err != nil {
err = fmt.Errorf("Not able to create %s dir: %v", secretKeyPairLocation, err)
log.Println(err)
return err
}
// generating and storing key-pair
err1 := api.GenerateAndStoreSecretKeypair(secretKeyPairLocation)
if err1 != nil {
err1 = fmt.Errorf("Not able to generate and store key pair for encryption")
log.Println(err1)
return err1
}
}
var rsaPublicKey *rsa.PublicKey
var e1 error
// Read Public Key
publicKeybytes, err2 := api.ReadKeys(publicKeyFilePath)
if err2 != nil {
api.LogDebugMessage("Not able to read public key")
return err2
}
// api.LogDebugMessage("PublicKey: %+v", publicKeybytes)
// convert []byte into RSA public key object
rsaPublicKey, e1 = api.DecodeToPublicKey(publicKeybytes)
if e1 != nil {
return e1
}
resultArgs, err := api.ProcessConfigArgs(args)
if err != nil {
return err
}
for _, ra := range resultArgs {
// Metadata name in qliksense CR is the name of the current context
api.LogDebugMessage("Trying to retreive current context: %+v ----- %s", qliksenseCR.Metadata.Name, qliksenseContextsFile)
// encrypt value with RSA key pair
valueBytes := []byte(ra.Value)
cipherText, e2 := api.Encrypt(valueBytes, rsaPublicKey)
if e2 != nil {
return e2
}
api.LogDebugMessage("Returned cipher text: %s", b64.StdEncoding.EncodeToString(cipherText))
if isK8sSecret {
// TODO: store the key value in k8s as secret
api.LogDebugMessage("Need to create a Kubernetes secret")
}
// TODO: Extend AddToSecrets to support adding k8s key ref. (OR) create a separate method to support that
// store the encrypted value in the file (OR)
// TODO: k8s secret name in the file
qliksenseCR.Spec.AddToSecrets(ra.SvcName, ra.Key, string(cipherText))
}
// write modified content into context.yaml
api.WriteToFile(&qliksenseCR, qliksenseContextsFile)
return nil
}
// SetConfigs - set-configs <key>=<value> commands
func (q *Qliksense) SetConfigs(args []string) error {
// retieve current context from config.yaml
qliksenseCR, qliksenseContextsFile, err := retrieveCurrentContextInfo(q)
if err != nil {
return err
}
resultArgs, err := api.ProcessConfigArgs(args)
if err != nil {
return err
}
for _, ra := range resultArgs {
qliksenseCR.Spec.AddToConfigs(ra.SvcName, ra.Key, ra.Value)
}
// write modified content into context.yaml
api.WriteToFile(&qliksenseCR, qliksenseContextsFile)
return nil
}
func retrieveCurrentContextInfo(q *Qliksense) (api.QliksenseCR, string, error) {
var qliksenseConfig api.QliksenseConfig
qliksenseConfigFile := filepath.Join(q.QliksenseHome, QliksenseConfigFile)
api.ReadFromFile(&qliksenseConfig, qliksenseConfigFile)
currentContext := qliksenseConfig.Spec.CurrentContext
api.LogDebugMessage("Current-context from config.yaml: %s", currentContext)
if currentContext == "" {
// current-context is empty
err := fmt.Errorf(`Please run the "qliksense config set-context <context-name>" first before viewing the current context info`)
log.Println(err)
return api.QliksenseCR{}, "", err
}
// read the context.yaml file
var qliksenseCR api.QliksenseCR
if currentContext == "" {
// current-context is empty
err := fmt.Errorf(`Please run the "qliksense config set-context <context-name>" first before viewing the current context info`)
log.Println(err)
return api.QliksenseCR{}, "", err
}
qliksenseContextsFile := filepath.Join(q.QliksenseHome, QliksenseContextsDir, currentContext, currentContext+".yaml")
if !api.FileExists(qliksenseContextsFile) {
err := fmt.Errorf("Context file does not exist.\nPlease try re-running `qliksense config set-context <context-name>` and then `qliksense config view` again")
log.Println(err)
return api.QliksenseCR{}, "", err
}
api.ReadFromFile(&qliksenseCR, qliksenseContextsFile)
api.LogDebugMessage("Read context file: %s, Read QliksenseCR: %v", qliksenseContextsFile, qliksenseCR)
return qliksenseCR, qliksenseContextsFile, nil
}
// SetOtherConfigs - set profile/namespace/storageclassname/git.repository commands
func (q *Qliksense) SetOtherConfigs(args []string) error {
// retieve current context from config.yaml
qliksenseCR, qliksenseContextsFile, err := retrieveCurrentContextInfo(q)
if err != nil {
return err
}
// modify appropriate fields
if len(args) == 0 {
err := fmt.Errorf("No args were provided. Please provide args to configure the current context")
log.Println(err)
return err
}
for _, arg := range args {
argsString := strings.Split(arg, "=")
switch argsString[0] {
case "profile":
qliksenseCR.Spec.Profile = argsString[1]
api.LogDebugMessage("Current profile after modification: %s ", qliksenseCR.Spec.Profile)
case "namespace":
qliksenseCR.Spec.NameSpace = argsString[1]
api.LogDebugMessage("Current namespace after modification: %s ", qliksenseCR.Spec.NameSpace)
case "git.repository":
qliksenseCR.Spec.Git.Repository = argsString[1]
api.LogDebugMessage("Current git repository after modification: %s ", qliksenseCR.Spec.Git.Repository)
case "storageClassName":
qliksenseCR.Spec.StorageClassName = argsString[1]
api.LogDebugMessage("Current StorageClassName after modification: %s ", qliksenseCR.Spec.StorageClassName)
case "rotateKeys":
rotateKeys, err := validateInput(argsString[1])
if err != nil {
return err
}
qliksenseCR.Spec.RotateKeys = rotateKeys
api.LogDebugMessage("Current rotateKeys after modification: %s ", qliksenseCR.Spec.RotateKeys)
default:
log.Println("As part of the `qliksense config set` command, please enter one of: profile, namespace, storageClassName,rotateKeys or git.repository arguments")
}
}
// write modified content into context.yaml
api.WriteToFile(&qliksenseCR, qliksenseContextsFile)
return nil
}
// SetContextConfig - set the context for qliksense kubernetes resources to live in
func (q *Qliksense) SetContextConfig(args []string) error {
if len(args) == 1 {
q.SetUpQliksenseContext(args[0], false)
} else {
err := fmt.Errorf("Please provide a name to configure the context with")
log.Println(err)
return err
}
return nil
}
func (q *Qliksense) ListContextConfigs() error {
qliksenseConfigFile := filepath.Join(q.QliksenseHome, QliksenseConfigFile)
var qliksenseConfig api.QliksenseConfig
api.ReadFromFile(&qliksenseConfig, qliksenseConfigFile)
out := ansi.NewColorableStdout()
w := tabwriter.NewWriter(out, 5, 8, 0, '\t', 0)
fmt.Fprintln(w, chalk.Underline.TextStyle("Context Name"), "\t", chalk.Underline.TextStyle("CR File Location"))
w.Flush()
if len(qliksenseConfig.Spec.Contexts) > 0 {
for _, cont := range qliksenseConfig.Spec.Contexts {
fmt.Fprintln(w, cont.Name, "\t", cont.CrFile, "\t")
}
w.Flush()
fmt.Fprintln(out, "")
fmt.Fprintln(out, chalk.Bold.TextStyle("Current Context : "), qliksenseConfig.Spec.CurrentContext)
} else {
fmt.Fprintln(out, "No Contexts Available")
}
return nil
}
// SetUpQliksenseDefaultContext - to setup dir structure for default qliksense context
func (q *Qliksense) SetUpQliksenseDefaultContext() error {
return q.SetUpQliksenseContext(DefaultQliksenseContext, true)
}
// SetUpQliksenseContext - to setup qliksense context
func (q *Qliksense) SetUpQliksenseContext(contextName string, isDefaultContext bool) error {
// check the length of the context name entered by the user, it should not exceed 17 chars
if len(contextName) > MaxContextNameLength {
err := fmt.Errorf("Please enter a context-name with utmost 17 characters")
log.Println(err)
return err
}
qliksenseConfigFile := filepath.Join(q.QliksenseHome, QliksenseConfigFile)
var qliksenseConfig api.QliksenseConfig
configFileTrack := false
if !api.FileExists(qliksenseConfigFile) {
qliksenseConfig.AddBaseQliksenseConfigs(contextName)
} else {
api.ReadFromFile(&qliksenseConfig, qliksenseConfigFile)
if isDefaultContext { // if config file exits but a default context is requested, we want to prevent writing to config file
configFileTrack = true
}
}
// creating a file in the name of the context if it does not exist/ opening it to append/modify content if it already exists
qliksenseContextsDir1 := filepath.Join(q.QliksenseHome, QliksenseContextsDir)
if !api.DirExists(qliksenseContextsDir1) {
if err := os.Mkdir(qliksenseContextsDir1, os.ModePerm); err != nil {
err = fmt.Errorf("Not able to create %s dir: %v", qliksenseContextsDir1, err)
log.Println(err)
return err
}
}
api.LogDebugMessage("%s exists", qliksenseContextsDir1)
// creating contexts/qlik-default/qlik-default.yaml file
qliksenseContextFile := filepath.Join(qliksenseContextsDir1, contextName, contextName+".yaml")
var qliksenseCR api.QliksenseCR
defaultContextsDir := filepath.Join(qliksenseContextsDir1, contextName)
if !api.DirExists(defaultContextsDir) {
if err := os.Mkdir(defaultContextsDir, os.ModePerm); err != nil {
err = fmt.Errorf("Not able to create %s: %v", defaultContextsDir, err)
log.Println(err)
return err
}
}
api.LogDebugMessage("%s exists", defaultContextsDir)
if !api.FileExists(qliksenseContextFile) {
qliksenseCR.AddCommonConfig(contextName)
api.LogDebugMessage("Added Context: %s", contextName)
} else {
api.ReadFromFile(&qliksenseCR, qliksenseContextFile)
}
api.WriteToFile(&qliksenseCR, qliksenseContextFile)
ctxTrack := false
if len(qliksenseConfig.Spec.Contexts) > 0 {
for _, ctx := range qliksenseConfig.Spec.Contexts {
if ctx.Name == contextName {
ctx.CrFile = qliksenseContextFile
ctxTrack = true
break
}
}
}
if !ctxTrack {
qliksenseConfig.Spec.Contexts = append(qliksenseConfig.Spec.Contexts, api.Context{
Name: contextName,
CrFile: qliksenseContextFile,
})
}
qliksenseConfig.Spec.CurrentContext = contextName
if !configFileTrack {
api.WriteToFile(&qliksenseConfig, qliksenseConfigFile)
}
return nil
}
func validateInput(input string) (string, error) {
var err error
validInputs := []string{"yes", "no", "None"}
isValid := false
for _, elem := range validInputs {
if input == elem {
isValid = true
break
}
}
if !isValid {
err = fmt.Errorf("Please enter one of: yes, no or None")
log.Println(err)
}
return input, err
}

View File

@@ -1,221 +0,0 @@
package qliksense
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"testing"
)
var (
testDir = "./tests"
)
func setup() func() {
// create tests dir
if err := os.Mkdir(testDir, 0777); err != nil {
log.Printf("\nError occurred: %v", err)
}
config :=
`
apiVersion: config.qlik.com/v1
kind: QliksenseConfig
metadata:
name: qliksenseConfig
spec:
contexts:
- name: qlik-default
crLocation: /root/.qliksense/contexts/qlik-default.yaml
currentContext: qlik-default
`
configFile := filepath.Join(testDir, "config.yaml")
// tests/config.yaml exists
ioutil.WriteFile(configFile, []byte(config), 0777)
contextYaml :=
`
apiVersion: qlik.com/v1
kind: Qliksense
metadata:
name: qlik-default
spec:
profile: docker-desktop
rotateKeys: "yes"
releaseName: qlik-default
`
qlikDefaultContext := "qlik-default"
// create contexts/qlik-default/ under tests/
contexts := "contexts"
contextsDir := filepath.Join(testDir, contexts, qlikDefaultContext)
if err := os.MkdirAll(contextsDir, 0777); err != nil {
err = fmt.Errorf("Not able to create directories")
log.Fatal(err)
}
contextFile := filepath.Join(contextsDir, qlikDefaultContext+".yaml")
ioutil.WriteFile(contextFile, []byte(contextYaml), 0777)
tearDown := func() {
os.RemoveAll(testDir)
}
return tearDown
}
func Test_retrieveCurrentContextInfo(t *testing.T) {
tearDown := setup()
defer tearDown()
q := &Qliksense{
QliksenseHome: testDir,
}
_, _, err := retrieveCurrentContextInfo(q)
if err != nil {
t.FailNow()
}
}
func TestSetUpQliksenseContext(t *testing.T) {
type args struct {
qlikSenseHome string
contextName string
isDefaultContext bool
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "valid contextname",
args: args{
qlikSenseHome: testDir,
contextName: "testContext1",
isDefaultContext: false,
},
wantErr: false,
},
{
name: "invalid contextname",
args: args{
qlikSenseHome: testDir,
contextName: "testContext_abcdefgh",
isDefaultContext: false,
},
wantErr: true,
},
}
tearDown := setup()
defer tearDown()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
q, err := New(tt.args.qlikSenseHome)
if err != nil {
t.Errorf("unable to create a qliksense instance")
return
}
if err := q.SetUpQliksenseContext(tt.args.contextName, tt.args.isDefaultContext); (err != nil) != tt.wantErr {
t.Errorf("SetUpQliksenseContext() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestSetUpQliksenseDefaultContext(t *testing.T) {
type args struct {
qlikSenseHome string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "valid case",
args: args{
qlikSenseHome: testDir,
},
wantErr: false,
},
}
tearDown := setup()
defer tearDown()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
q, err := New(tt.args.qlikSenseHome)
if err != nil {
t.Errorf("unable to create a qliksense instance")
return
}
if err := q.SetUpQliksenseDefaultContext(); (err != nil) != tt.wantErr {
t.Errorf("SetUpQliksenseDefaultContext() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestSetOtherConfigs(t *testing.T) {
type args struct {
q *Qliksense
args []string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "valid case",
args: args{
q: &Qliksense{
QliksenseHome: testDir,
},
args: []string{"profile=minikube", "namespace=qliksense", "storageClassName=efs"},
},
wantErr: false,
},
}
tearDown := setup()
defer tearDown()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.args.q.SetOtherConfigs(tt.args.args); (err != nil) != tt.wantErr {
t.Errorf("SetOtherConfigs() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestSetConfigs(t *testing.T) {
type args struct {
q *Qliksense
args []string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "valid case",
args: args{
q: &Qliksense{
QliksenseHome: testDir,
},
args: []string{"qliksense[name=acceptEULA]=\"yes\"", "qliksense[name=mongoDbUri]=\"mongo://mongo:3307\""},
},
wantErr: false,
},
}
tearDown := setup()
defer tearDown()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.args.q.SetConfigs(tt.args.args); (err != nil) != tt.wantErr {
t.Errorf("SetConfigs() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View File

@@ -29,7 +29,7 @@ import (
)
// PullImages ...
func (q *Qliksense) PullImages(gitRef, profile string, engine bool) error {
func (p *Qliksense) PullImages(gitRef, profile string, engine bool) error {
var (
image, versionFile, imagesDir, homeDir string
err error
@@ -38,7 +38,7 @@ func (q *Qliksense) PullImages(gitRef, profile string, engine bool) error {
println("getting images list...")
// TODO: get getref and profile from config/cr for About function call
if versionOut, err = q.About(gitRef, profile); err != nil {
if versionOut, err = p.About(gitRef, profile); err != nil {
return err
}
@@ -61,7 +61,7 @@ func (q *Qliksense) PullImages(gitRef, profile string, engine bool) error {
}
}
for _, image = range versionOut.Images {
if _, err = q.PullImage(image, engine); err != nil {
if _, err = p.PullImage(image, engine); err != nil {
fmt.Print(err)
}
println("---")
@@ -71,14 +71,14 @@ func (q *Qliksense) PullImages(gitRef, profile string, engine bool) error {
}
// PullImage ...
func (q *Qliksense) PullImage(imageName string, engine bool) (map[string]string, error) {
func (p *Qliksense) PullImage(imageName string, engine bool) (map[string]string, error) {
if engine {
return q.pullDockerImage(imageName)
return p.pullDockerImage(imageName)
}
return q.pullImage(imageName)
return p.pullImage(imageName)
}
func (q *Qliksense) commandTimeoutContext(commandTimeout time.Duration) (context.Context, context.CancelFunc) {
func (p *Qliksense) commandTimeoutContext(commandTimeout time.Duration) (context.Context, context.CancelFunc) {
ctx := context.Background()
var cancel context.CancelFunc = func() {}
if commandTimeout > 0 {
@@ -87,7 +87,7 @@ func (q *Qliksense) commandTimeoutContext(commandTimeout time.Duration) (context
return ctx, cancel
}
func (q *Qliksense) pullImage(imageName string) (map[string]string, error) {
func (p *Qliksense) pullImage(imageName string) (map[string]string, error) {
var (
ctx context.Context
cancel context.CancelFunc
@@ -98,7 +98,7 @@ func (q *Qliksense) pullImage(imageName string) (map[string]string, error) {
err error
policyContext *signature.PolicyContext
)
ctx, cancel = q.commandTimeoutContext(0)
ctx, cancel = p.commandTimeoutContext(0)
defer cancel()
if srcRef, err = alltransports.ParseImageName("docker://" + imageName); err != nil {
@@ -141,7 +141,7 @@ func (q *Qliksense) pullImage(imageName string) (map[string]string, error) {
})
return nil, err
}
func (q *Qliksense) pullDockerImage(imageName string) (map[string]string, error) {
func (p *Qliksense) pullDockerImage(imageName string) (map[string]string, error) {
var (
cli *command.DockerCli
dockerOutput io.Writer
@@ -156,7 +156,7 @@ func (q *Qliksense) pullDockerImage(imageName string) (map[string]string, error)
termFd uintptr
err error
)
ctx, cancel = q.commandTimeoutContext(0)
ctx, cancel = p.commandTimeoutContext(0)
defer cancel()
if cli, err = command.NewDockerCli(); err != nil {
@@ -208,7 +208,7 @@ func (q *Qliksense) pullDockerImage(imageName string) (map[string]string, error)
}
//TagAndPushImages ...
func (q *Qliksense) TagAndPushImages(registry string, engine bool) error {
func (p *Qliksense) TagAndPushImages(registry string, engine bool) error {
var (
image string
err error
@@ -221,7 +221,7 @@ func (q *Qliksense) TagAndPushImages(registry string, engine bool) error {
}
for _, image = range images.Images {
if err = q.TagAndPush(image, registry, engine); err != nil {
if err = p.TagAndPush(image, registry, engine); err != nil {
fmt.Print(err)
}
println("---")
@@ -230,7 +230,7 @@ func (q *Qliksense) TagAndPushImages(registry string, engine bool) error {
return nil
}
func (q *Qliksense) directoryExists(path string) (exists bool, err error) {
func (p *Qliksense) directoryExists(path string) (exists bool, err error) {
if info, err := os.Stat(path); err != nil && os.IsNotExist(err) {
exists = false
err = nil
@@ -246,14 +246,14 @@ func (q *Qliksense) directoryExists(path string) (exists bool, err error) {
}
//TagAndPush ...
func (q *Qliksense) TagAndPush(image string, registryName string, engine bool) error {
func (p *Qliksense) TagAndPush(image string, registryName string, engine bool) error {
if engine {
return q.tagAndDockerPush(image, registryName)
return p.tagAndDockerPush(image, registryName)
}
return q.tagAndPush(image, registryName)
return p.tagAndPush(image, registryName)
}
func (q *Qliksense) tagAndPush(image string, registryName string) error {
func (p *Qliksense) tagAndPush(image string, registryName string) error {
var (
ctx context.Context
cancel context.CancelFunc
@@ -265,7 +265,7 @@ func (q *Qliksense) tagAndPush(image string, registryName string) error {
policyContext *signature.PolicyContext
srcExists bool
)
ctx, cancel = q.commandTimeoutContext(0)
ctx, cancel = p.commandTimeoutContext(0)
defer cancel()
segments = strings.Split(image, "/")
@@ -278,11 +278,11 @@ func (q *Qliksense) tagAndPush(image string, registryName string) error {
return err
}
srcDir = filepath.Join(homeDir, ".qliksense", "images", nameTag[0], nameTag[1])
if srcExists, err = q.directoryExists(srcDir); err != nil {
if srcExists, err = p.directoryExists(srcDir); err != nil {
return err
}
if !srcExists {
if _, err = q.PullImage(image, false); err != nil {
if _, err = p.PullImage(image, false); err != nil {
return err
}
}
@@ -323,7 +323,7 @@ func (q *Qliksense) tagAndPush(image string, registryName string) error {
}
// PullImage ...
func (q *Qliksense) tagAndDockerPush(image string, registryName string) error {
func (p *Qliksense) tagAndDockerPush(image string, registryName string) error {
var (
cli *command.DockerCli
dockerOutput io.Writer
@@ -344,7 +344,7 @@ func (q *Qliksense) tagAndDockerPush(image string, registryName string) error {
err error
)
// TODO: Create a real cli config context
ctx, cancel = q.commandTimeoutContext(0)
ctx, cancel = p.commandTimeoutContext(0)
defer cancel()
if cli, err = command.NewDockerCli(); err != nil {
return err

View File

@@ -2,39 +2,9 @@ package qliksense
import (
"fmt"
"github.com/google/uuid"
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
)
const (
QLIK_GIT_REPO = "https://github.com/qlik-oss/qliksense-k8s"
)
func (q *Qliksense) FetchQK8s(version string) error {
qConfig := qapi.NewQConfig(q.QliksenseHome)
return fetchAndUpdateCR(qConfig, version)
}
func fetchAndUpdateCR(qConfig *qapi.QliksenseConfig, version string) error {
qcr, err := qConfig.GetCurrentCR()
if err != nil {
fmt.Println("cannot get the current-context cr", err)
return err
}
if qConfig.IsRepoExistForCurrent(version) {
return nil
}
destDir := qConfig.BuildRepoPath(version)
fmt.Printf("fetching version [%s] from %s\n", version, QLIK_GIT_REPO)
if repo, err := kapis_git.CloneRepository(destDir, QLIK_GIT_REPO, nil); err != nil {
return err
} else if err = kapis_git.Checkout(repo, version, fmt.Sprintf("%v-by-operator-%v", version, uuid.New().String()), nil); err != nil {
return err
}
qcr.Spec.ManifestsRoot = qConfig.BuildCurrentManifestsRoot(version)
qcr.AddLabelToCr("version", version)
return qConfig.WriteCurrentContextCR(qcr)
func (q *Qliksense) FetchQK8s(version string) {
//io.WriteString(os.Stdout, q.GetCRDString())
fmt.Println(version)
}

View File

@@ -2,28 +2,21 @@ package qliksense
import (
"fmt"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
)
type InstallCommandOptions struct {
AcceptEULA string
Namespace string
StorageClass string
MongoDbUri string
RotateKeys string
}
func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions) error {
func (q *Qliksense) InstallQK8s(version string) error {
// step1: fetch 1.0.0 # pull down qliksense-k8s@1.0.0
// step2: operator view | kubectl apply -f # operator manifest (CRD)
// step3: config apply | kubectl apply -f # generates patches (if required) in configuration directory, applies manifest
// step4: config view | kubectl apply -f # generates Custom Resource manifest (CR)
// fetch the version
qConfig := qapi.NewQConfig(q.QliksenseHome)
fetchAndUpdateCR(qConfig, version)
//io.WriteString(os.Stdout, q.GetCRDString())
//fmt.Println(version)
fmt.Println("Fetching " + version)
//qConfig := qapi.NewQConfig(q.QliksenseHome)
//qcr, err := qConfig.GetCurrentCR()
//TODO: may need to check if CRD already installed, but doing apply does not hurt for now
//install crd into cluster
@@ -34,28 +27,13 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions) err
}
// install generated manifests into cluster
fmt.Println("Installing generated manifests into cluster")
qConfig := qapi.NewQConfig(q.QliksenseHome)
qcr, err := qConfig.GetCurrentCR()
if err != nil {
fmt.Println("cannot get the current-context cr", err)
return err
}
if opts.AcceptEULA != "" {
qcr.Spec.AddToConfigs("qliksense", "acceptEULA", opts.AcceptEULA)
}
if opts.MongoDbUri != "" {
qcr.Spec.AddToSecrets("qliksense", "mongoDbUri", opts.MongoDbUri)
}
if opts.StorageClass != "" {
qcr.Spec.StorageClassName = opts.StorageClass
}
if opts.Namespace != "" {
qcr.Spec.NameSpace = opts.Namespace
}
if opts.RotateKeys != "" {
qcr.Spec.RotateKeys = opts.RotateKeys
}
qConfig.WriteCurrentContextCR(qcr)
if err := q.applyConfigToK8s(qcr, "install"); err != nil {
if err := applyConfigToK8s(qcr); err != nil {
fmt.Println("cannot do kubectl apply on manifests")
return err
}

View File

@@ -1,22 +1,11 @@
package qliksense
import (
"bytes"
"encoding/base64"
"io"
"io/ioutil"
"log"
"os"
"path"
"strings"
"testing"
"gopkg.in/yaml.v3"
"github.com/Shopify/ejson"
"github.com/qlik-oss/k-apis/pkg/config"
"github.com/qlik-oss/k-apis/pkg/qust"
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
)
@@ -58,7 +47,11 @@ metadata:
}
}
func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
func Test_executeKustomizeBuild_onQlikConfig_DISABLED(t *testing.T) {
if testing.Short() {
t.Skip("skipping in short mode")
}
tmpDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("unexpected error: %v\n", err)
@@ -72,70 +65,7 @@ func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
t.Fatalf("unexpected error: %v\n", err)
}
cr := &config.CRSpec{
ManifestsRoot: configPath,
}
if err := os.Setenv("EJSON_KEYDIR", tmpDir); err != nil {
t.Fatalf("unexpected error setting EJSON_KEYDIR environment variable: %v\n", err)
}
if err := os.Unsetenv("EJSON_KEY"); err != nil {
t.Fatalf("unexpected error unsetting EJSON_KEY: %v\n", err)
}
generateKeys(cr, "won't-use")
yamlResources, err := executeKustomizeBuild(path.Join(configPath, "manifests", "base", "resources", "users"))
if err != nil {
if _, err := executeKustomizeBuild(path.Join(configPath, "manifests", "base")); err != nil {
t.Fatalf("unexpected kustomize error: %v\n", err)
}
decoder := yaml.NewDecoder(bytes.NewReader(yamlResources))
var resource map[string]interface{}
keyIdBase64 := ""
for {
err := decoder.Decode(&resource)
if err != nil {
if err != io.EOF {
t.Fatalf("unexpected yaml decode error: %v\n", err)
}
break
}
if resource["kind"].(string) == "Secret" && strings.Contains(resource["metadata"].(map[string]interface{})["name"].(string), "users-secrets-") {
keyIdBase64 = resource["data"].(map[string]interface{})["tokenAuthPrivateKeyId"].(string)
break
}
}
untransformedKeyId := `(( (ds "data").kid ))`
if keyIdBase64 == "" {
t.Fatalf("expected keyIdBase64 for users secret to be non empty:\n")
} else if keyId, err := base64.StdEncoding.DecodeString(keyIdBase64); err != nil {
t.Fatalf("unexpected base64 decode error: %v\n", err)
} else if string(keyId) == untransformedKeyId {
t.Fatalf("unexpected users keyId: %v\n", untransformedKeyId)
}
}
func generateKeys(cr *config.CRSpec, defaultKeyDir string) {
log.Println("rotating all keys")
keyDir := getEjsonKeyDir(defaultKeyDir)
if ejsonPublicKey, ejsonPrivateKey, err := ejson.GenerateKeypair(); err != nil {
log.Printf("error generating an ejson key pair: %v\n", err)
} else if err := qust.GenerateKeys(cr, ejsonPublicKey); err != nil {
log.Printf("error generating application keys: %v\n", err)
} else if err := os.MkdirAll(keyDir, os.ModePerm); err != nil {
log.Printf("error makeing sure private key storage directory: %v exists, error: %v\n", keyDir, err)
} else if err := ioutil.WriteFile(path.Join(keyDir, ejsonPublicKey), []byte(ejsonPrivateKey), os.ModePerm); err != nil {
log.Printf("error storing ejson private key: %v\n", err)
}
}
func getEjsonKeyDir(defaultKeyDir string) string {
ejsonKeyDir := os.Getenv("EJSON_KEYDIR")
if ejsonKeyDir == "" {
ejsonKeyDir = defaultKeyDir
}
return ejsonKeyDir
}

View File

@@ -2,7 +2,6 @@ package qliksense
import (
"fmt"
"path/filepath"
"io"
"os"
"strings"
@@ -33,7 +32,7 @@ func (q *Qliksense) getYamlFromPackrFile(packrFile string) string {
func (q *Qliksense) getFileList(resourceType string) []string {
var resList []string
for _, v := range q.CrdBox.List() {
if strings.Contains(v, filepath.Join(resourceType, "")) {
if strings.Contains(v, resourceType+"/") {
resList = append(resList, []string{v}...)
}
}

View File

@@ -2,29 +2,19 @@
package qliksense
import (
"os"
"path"
"github.com/gobuffalo/packr/v2"
)
// Qliksense is the logic behind the qliksense client
type Qliksense struct {
QliksenseHome string
QliksenseEjsonKeyDir string
CrdBox *packr.Box ``
QliksenseHome string
CrdBox *packr.Box
}
// New qliksense client, initialized with useful defaults.
func New(qliksenseHome string) (*Qliksense, error) {
qliksenseClient := &Qliksense{
func New(qliksenseHome string) *Qliksense {
return &Qliksense{
QliksenseHome: qliksenseHome,
CrdBox: packr.New("crds", "./crds"),
}
qliksenseClient.QliksenseEjsonKeyDir = path.Join(qliksenseHome, "ejson", "keys")
if err := os.MkdirAll(qliksenseClient.QliksenseEjsonKeyDir, os.ModePerm); err != nil {
return nil, err
}
return qliksenseClient, nil
}

View File

@@ -1,21 +0,0 @@
package qliksense
import (
"errors"
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")
}
str, err := q.getCRString(contextName)
if err != nil {
return err
}
return qapi.KubectlDelete(str)
}

View File

@@ -1,39 +0,0 @@
package qliksense
import (
"fmt"
qapi "github.com/qlik-oss/sense-installer/pkg/api"
)
func (q *Qliksense) UpgradeQK8s() error {
// step1: get CR
// step2: run kustomize
// step3: run kubectl apply
// fetch the version
qConfig := qapi.NewQConfig(q.QliksenseHome)
qcr, err := qConfig.GetCurrentCR()
if err != nil {
fmt.Println("cannot get the current-context cr", err)
return err
}
qcr.Spec.RotateKeys = "no"
if err := q.applyConfigToK8s(qcr, "upgrade"); err != nil {
fmt.Println("cannot do kubectl apply on manifests")
return err
}
fmt.Println("Install operator CR into cluster")
r, err := qcr.GetString()
if err != nil {
return err
}
if err := qapi.KubectlApply(r); err != nil {
fmt.Println("cannot do kubectl apply on operator CR")
}
return nil
}