Compare commits
64 Commits
export_fun
...
fix-kust
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d8f6dce51 | ||
|
|
6ab9317638 | ||
|
|
5899760c16 | ||
|
|
a63c400106 | ||
|
|
568012edd8 | ||
|
|
34f702b183 | ||
|
|
d8cdbb30bb | ||
|
|
616e759089 | ||
|
|
c2d3f39542 | ||
|
|
0b6c1b8bc9 | ||
|
|
7091da099c | ||
|
|
1f8bcf3469 | ||
|
|
627a4a14f9 | ||
|
|
66333e9e97 | ||
|
|
36c91e9dab | ||
|
|
b6ae0c9873 | ||
|
|
6899a7be77 | ||
|
|
373d6499dc | ||
|
|
746823c54b | ||
|
|
73b5da8c14 | ||
|
|
7cc6e55779 | ||
|
|
27c8f3ee8e | ||
|
|
cb9f463f01 | ||
|
|
293e923c82 | ||
|
|
504190c75c | ||
|
|
fc62e5fea4 | ||
|
|
bf4e4b9bf1 | ||
|
|
4cb3231a08 | ||
|
|
e1dbcfaac8 | ||
|
|
77a3bf4581 | ||
|
|
a670b6c750 | ||
|
|
492e4a1baa | ||
|
|
3b54a7f0b2 | ||
|
|
55cfc42257 | ||
|
|
12e2bec618 | ||
|
|
2ed59321e4 | ||
|
|
98198a3c8b | ||
|
|
afab3e2939 | ||
|
|
62fda8f2c6 | ||
|
|
6af87ab00a | ||
|
|
0b60838b52 | ||
|
|
bb2974fe66 | ||
|
|
97b2239c2e | ||
|
|
9eff54d9ec | ||
|
|
2d0a2a32bf | ||
|
|
d7238e2b3c | ||
|
|
1c0ded7f3d | ||
|
|
ec8a9376e7 | ||
|
|
bcc321e180 | ||
|
|
0aabf63715 | ||
|
|
c9ca5c8be0 | ||
|
|
9d0ac0290f | ||
|
|
dd8a48b2b8 | ||
|
|
9fb6800993 | ||
|
|
bbb811a879 | ||
|
|
8156b884ce | ||
|
|
7525c2e698 | ||
|
|
60763e034a | ||
|
|
ce4081a422 | ||
|
|
dd503a40c1 | ||
|
|
b790419fc2 | ||
|
|
55f9c07c21 | ||
|
|
ef77ea3a5f | ||
|
|
7f70bfc7de |
2
.github/workflows/mkdocs.yml
vendored
2
.github/workflows/mkdocs.yml
vendored
@@ -16,6 +16,6 @@ jobs:
|
|||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
- name: Deploy docs
|
- name: Deploy docs
|
||||||
uses: mhausenblas/mkdocs-deploy-gh-pages@1.11
|
uses: mhausenblas/mkdocs-deploy-gh-pages@1.12
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func about(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
|
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "about ref",
|
Use: "about ref",
|
||||||
Short: "Displays information pertaining to Qliksense on Kubernetes",
|
Short: "Displays information pertaining to qliksense on Kubernetes",
|
||||||
Long: "Gives the version of QLik Sense on Kubernetes and versions of images.",
|
Long: "Gives the version of QLik Sense on Kubernetes and versions of images.",
|
||||||
Example: `
|
Example: `
|
||||||
qliksense about 1.0.0
|
qliksense about 1.0.0
|
||||||
|
|||||||
@@ -1,74 +1,44 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
|
||||||
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func applyCmd(q *qliksense.Qliksense) *cobra.Command {
|
func applyCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
opts := &qliksense.InstallCommandOptions{}
|
opts := &qliksense.InstallCommandOptions{
|
||||||
|
CleanPatchFiles: true,
|
||||||
|
}
|
||||||
filePath := ""
|
filePath := ""
|
||||||
keepPatchFiles, pull, push := false, false, false
|
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "apply",
|
Use: "apply",
|
||||||
Short: "install qliksense based on provided cr file",
|
Short: "install qliksense based on provided cr file",
|
||||||
Long: `install qliksense based on provided cr file`,
|
Long: `install qliksense based on provided cr file`,
|
||||||
Example: `qliksense apply -f file_name or cat cr_file | qliksense apply -f -`,
|
Example: `qliksense apply -f file_name or cat cr_file | qliksense apply -f -`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return runLoadOrApplyCommandE(cmd, func(crBytes []byte) error {
|
return apply(q, cmd, opts)
|
||||||
if cr, crBytesWithEula, err := getCrWithEulaInserted(crBytes); err != nil {
|
|
||||||
return err
|
|
||||||
} else if err := validatePullPushFlagsOnApply(cr, pull, push); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
return q.ApplyCRFromReader(bytes.NewReader(crBytesWithEula), opts, keepPatchFiles, true, pull, push)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
f := c.Flags()
|
f := c.Flags()
|
||||||
f.StringVarP(&filePath, "file", "f", "", "Install from a CR file")
|
f.StringVarP(&filePath, "file", "f", "", "Install from a CR file")
|
||||||
c.MarkFlagRequired("file")
|
|
||||||
f.StringVarP(&opts.StorageClass, "storageClass", "s", "", "Storage class for 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.MongodbUri, "mongodbUri", "m", "", "mongodbUri for qliksense (i.e. mongodb://qlik-default-mongodb:27017/qliksense?ssl=false)")
|
||||||
f.StringVarP(&opts.RotateKeys, "rotateKeys", "r", "", "Rotate JWT keys for qliksense (yes:rotate keys/ no:use exising keys from cluster/ None: use default EJSON_KEY from env")
|
f.BoolVar(&opts.CleanPatchFiles, cleanPatchFilesFlagName, opts.CleanPatchFiles, cleanPatchFilesFlagUsage)
|
||||||
f.BoolVar(&keepPatchFiles, keepPatchFilesFlagName, keepPatchFiles, keepPatchFilesFlagUsage)
|
f.BoolVarP(&opts.Pull, pullFlagName, pullFlagShorthand, opts.Pull, pullFlagUsage)
|
||||||
f.BoolVarP(&pull, pullFlagName, pullFlagShorthand, pull, pullFlagUsage)
|
f.BoolVarP(&opts.Push, pushFlagName, pushFlagShorthand, opts.Push, pushFlagUsage)
|
||||||
f.BoolVarP(&push, pushFlagName, pushFlagShorthand, push, pushFlagUsage)
|
f.StringVarP(&opts.AcceptEULA, "acceptEULA", "a", opts.AcceptEULA, "Accept EULA for qliksense")
|
||||||
|
|
||||||
eulaPreRunHooks.addValidator(fmt.Sprintf("%v %v", rootCommandName, c.Name()), loadOrApplyCommandEulaPreRunHook)
|
|
||||||
|
|
||||||
|
if err := c.MarkFlagRequired("file"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func validatePullPushFlagsOnApply(cr *qapi.QliksenseCR, pull, push bool) error {
|
func apply(q *qliksense.Qliksense, cmd *cobra.Command, opts *qliksense.InstallCommandOptions) error {
|
||||||
if pull && !push {
|
if crBytes, err := getCrBytesFromFileFlag(cmd); err != nil {
|
||||||
fmt.Printf("WARNING: pulling images without pushing them")
|
return err
|
||||||
}
|
|
||||||
if push {
|
|
||||||
if registry := cr.Spec.GetImageRegistry(); registry == "" {
|
|
||||||
return errors.New("no image registry set in the CR; to set it use: qliksense config set-image-registry")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCrWithEulaInserted(crBytes []byte) (*qapi.QliksenseCR, []byte, error) {
|
|
||||||
if cr, err := qapi.CreateCRObjectFromString(string(crBytes)); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
} else {
|
} else {
|
||||||
cr.SetEULA("yes")
|
return q.ApplyCRFromBytes(crBytes, opts, true)
|
||||||
if crBytesWithEula, err := qapi.K8sToYaml(cr); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
} else {
|
|
||||||
return cr, crBytesWithEula, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,3 +220,27 @@ func cleanConfigRepoPatchesCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unsetCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "unset",
|
||||||
|
Short: "remove a key from a context or a secrets or a configs from the context",
|
||||||
|
Example: `
|
||||||
|
# remove the key from CR
|
||||||
|
qliksense config unset <key>
|
||||||
|
|
||||||
|
# remove the key from service inside configs/secrets of CR
|
||||||
|
qliksense config unset <service>.<key>
|
||||||
|
|
||||||
|
# remove the service from inside configs/secrets of CR
|
||||||
|
qliksense config usnet <servcie>
|
||||||
|
|
||||||
|
all of the above supports space separated multiple arguments
|
||||||
|
`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return q.UnsetCmd(args)
|
||||||
|
},
|
||||||
|
Args: cobra.MinimumNArgs(1),
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,31 +12,35 @@ var crdsCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func crdsViewCmd(q *qliksense.Qliksense) *cobra.Command {
|
func crdsViewCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
opts := &qliksense.CrdCommandOptions{}
|
opts := &qliksense.CrdCommandOptions{
|
||||||
|
All: true,
|
||||||
|
}
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "view",
|
Use: "view",
|
||||||
Short: "View CRDs for qliksense application. use view --all to see opearator crd as well ",
|
Short: "View CRDs for qliksense application. Use view --all=false to exclude the operator CRD",
|
||||||
Long: `View CRDs for qliksense application. use view --all to see opearator crd as well`,
|
Long: "View CRDs for qliksense application. Use view --all=false to exclude the operator CRD",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return q.ViewCrds(opts)
|
return q.ViewCrds(opts)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
f := c.Flags()
|
f := c.Flags()
|
||||||
f.BoolVarP(&opts.All, "all", "", false, "Include All CRDs")
|
f.BoolVarP(&opts.All, "all", "", opts.All, "If set to false, then the operator CRD is excluded")
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func crdsInstallCmd(q *qliksense.Qliksense) *cobra.Command {
|
func crdsInstallCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
opts := &qliksense.CrdCommandOptions{}
|
opts := &qliksense.CrdCommandOptions{
|
||||||
|
All: true,
|
||||||
|
}
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "install",
|
Use: "install",
|
||||||
Short: "Install CRDs fro Qliksense application. Use install --all to include operator crd",
|
Short: "Install CRDs for qliksense application. Use install --all=false to exclude the operator CRD",
|
||||||
Long: `Install CRDs fro Qliksense application. Use install --all to include operator crd`,
|
Long: "Install CRDs for qliksense application. Use install --all=false to exclude the operator CRD",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return q.InstallCrds(opts)
|
return q.InstallCrds(opts)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
f := c.Flags()
|
f := c.Flags()
|
||||||
f.BoolVarP(&opts.All, "all", "", false, "Include All CRDs")
|
f.BoolVarP(&opts.All, "all", "", opts.All, "If set to false, then the operator CRD is excluded")
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/mattn/go-tty"
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
type eulaPreRunHooksT struct {
|
|
||||||
validators map[string]func(cmd *cobra.Command, q *qliksense.Qliksense) (bool, error)
|
|
||||||
postValidationArtifacts map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *eulaPreRunHooksT) addValidator(command string, validator func(cmd *cobra.Command, q *qliksense.Qliksense) (bool, error)) {
|
|
||||||
e.validators[command] = validator
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *eulaPreRunHooksT) getValidator(command string) func(cmd *cobra.Command, q *qliksense.Qliksense) (bool, error) {
|
|
||||||
if validator, ok := e.validators[command]; ok {
|
|
||||||
return validator
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *eulaPreRunHooksT) addPostValidationArtifact(artifactName string, artifact interface{}) {
|
|
||||||
e.postValidationArtifacts[artifactName] = artifact
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *eulaPreRunHooksT) getPostValidationArtifact(artifactName string) interface{} {
|
|
||||||
if artifact, ok := e.postValidationArtifacts[artifactName]; ok {
|
|
||||||
return artifact
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var eulaEnforced = os.Getenv("QLIKSENSE_EULA_ENFORCE") == "true"
|
|
||||||
var eulaText = "Please read the end user license agreement at: https://www.qlik.com/us/legal/license-terms"
|
|
||||||
var eulaPrompt = "Do you accept our EULA? (y/n): "
|
|
||||||
var eulaErrorInstruction = `You must enter "y" to continue`
|
|
||||||
var eulaPreRunHooks = eulaPreRunHooksT{
|
|
||||||
validators: make(map[string]func(cmd *cobra.Command, q *qliksense.Qliksense) (bool, error)),
|
|
||||||
postValidationArtifacts: make(map[string]interface{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
func commandAlwaysRequiresEulaAcceptance(commandName string) bool {
|
|
||||||
return commandName == fmt.Sprintf("%v install", rootCommandName) ||
|
|
||||||
commandName == fmt.Sprintf("%v apply", rootCommandName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func globalEulaPreRun(cmd *cobra.Command, q *qliksense.Qliksense) {
|
|
||||||
if isEulaEnforced(cmd.CommandPath()) {
|
|
||||||
if strings.TrimSpace(strings.ToLower(cmd.Flag("acceptEULA").Value.String())) != "yes" {
|
|
||||||
if eulaPreRunHook := eulaPreRunHooks.getValidator(cmd.CommandPath()); eulaPreRunHook != nil {
|
|
||||||
if eulaAccepted, err := eulaPreRunHook(cmd, q); err != nil {
|
|
||||||
panic(err)
|
|
||||||
} else if !eulaAccepted {
|
|
||||||
doEnforceEula()
|
|
||||||
}
|
|
||||||
} else if qConfig, err := qapi.NewQConfigE(q.QliksenseHome); err != nil {
|
|
||||||
doEnforceEula()
|
|
||||||
} else if qcr, err := qConfig.GetCurrentCR(); err != nil || !qcr.IsEULA() {
|
|
||||||
doEnforceEula()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func globalEulaPostRun(cmd *cobra.Command, q *qliksense.Qliksense) {
|
|
||||||
if isEulaEnforced(cmd.CommandPath()) {
|
|
||||||
if err := q.SetEulaAccepted(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isEulaEnforced(commandName string) bool {
|
|
||||||
return eulaEnforced || commandAlwaysRequiresEulaAcceptance(commandName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doEnforceEula() {
|
|
||||||
fmt.Println(eulaText)
|
|
||||||
fmt.Print(eulaPrompt)
|
|
||||||
answer := readRuneFromTty()
|
|
||||||
if strings.ToLower(answer) != "y" {
|
|
||||||
fmt.Println(eulaErrorInstruction)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readRuneFromTty() string {
|
|
||||||
t, err := tty.Open()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer t.Close()
|
|
||||||
answer, err := t.ReadString()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return answer
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
func exportCmd(q *qliksense.Qliksense) *cobra.Command {
|
|
||||||
filePath := q.QliksenseHome
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "export",
|
|
||||||
Short: "export files for corresponding context",
|
|
||||||
Long: `exports all context files in zip format`,
|
|
||||||
Example: `qliksense export <context_name>`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
|
|
||||||
if len(args) != 0 {
|
|
||||||
context := args[0]
|
|
||||||
return q.ExportContext(context, filePath)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
f := c.Flags()
|
|
||||||
f.StringVarP(&filePath, "output", "o", "", "Output Directory")
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
|
||||||
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func installCmd(q *qliksense.Qliksense) *cobra.Command {
|
func installCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
opts := &qliksense.InstallCommandOptions{}
|
opts := &qliksense.InstallCommandOptions{
|
||||||
|
CleanPatchFiles: true,
|
||||||
|
}
|
||||||
filePath := ""
|
filePath := ""
|
||||||
keepPatchFiles, pull, push := false, false, false
|
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "install",
|
Use: "install",
|
||||||
Short: "install a qliksense release",
|
Short: "install a qliksense release",
|
||||||
@@ -22,74 +18,35 @@ func installCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
# qliksense install -f file_name or cat cr_file | qliksense install -f -
|
# qliksense install -f file_name or cat cr_file | qliksense install -f -
|
||||||
`,
|
`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
version := ""
|
||||||
|
if len(args) != 0 {
|
||||||
|
version = args[0]
|
||||||
|
}
|
||||||
|
|
||||||
if filePath != "" {
|
if filePath != "" {
|
||||||
return runLoadOrApplyCommandE(cmd, func(crBytes []byte) error {
|
if err := apply(q, cmd, opts); err != nil {
|
||||||
if cr, crBytesWithEula, err := getCrWithEulaInserted(crBytes); err != nil {
|
|
||||||
return err
|
|
||||||
} else if err := validatePullPushFlagsOnApply(cr, pull, push); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
return q.ApplyCRFromReader(bytes.NewReader(crBytesWithEula), opts, keepPatchFiles, true, pull, push)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
version := ""
|
|
||||||
if len(args) != 0 {
|
|
||||||
version = args[0]
|
|
||||||
}
|
|
||||||
if err := validatePullPushFlagsOnInstall(q, pull, push); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if pull {
|
} else {
|
||||||
fmt.Println("Pulling images...")
|
if err := q.InstallQK8s(version, opts); err != nil {
|
||||||
if err := q.PullImages(version, ""); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if push {
|
|
||||||
fmt.Println("Pushing images...")
|
|
||||||
if err := q.PushImagesForCurrentCR(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return q.InstallQK8s(version, opts, keepPatchFiles)
|
|
||||||
}
|
}
|
||||||
|
postflightChecksCmd := AllPostflightChecks(q)
|
||||||
|
postflightChecksCmd.DisableFlagParsing = true
|
||||||
|
return postflightChecksCmd.Execute()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
eulaPreRunHooks.addValidator(fmt.Sprintf("%v %v", rootCommandName, c.Name()), func(cmd *cobra.Command, q *qliksense.Qliksense) (b bool, err error) {
|
|
||||||
if filePath != "" {
|
|
||||||
return loadOrApplyCommandEulaPreRunHook(cmd, q)
|
|
||||||
} else if qConfig, err := qapi.NewQConfigE(q.QliksenseHome); err != nil {
|
|
||||||
return false, err
|
|
||||||
} else if qcr, err := qConfig.GetCurrentCR(); err != nil {
|
|
||||||
return false, err
|
|
||||||
} else {
|
|
||||||
return qcr.IsEULA(), nil
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
f := c.Flags()
|
f := c.Flags()
|
||||||
f.StringVarP(&opts.StorageClass, "storageClass", "s", "", "Storage class for qliksense")
|
|
||||||
f.StringVarP(&opts.MongoDbUri, "mongoDbUri", "m", "", "mongoDbUri for qliksense (i.e. mongodb://qlik-default-mongodb:27017/qliksense?ssl=false)")
|
|
||||||
f.StringVarP(&opts.RotateKeys, "rotateKeys", "r", "", "Rotate JWT keys for qliksense (yes:rotate keys/ no:use exising keys from cluster/ None: use default EJSON_KEY from env")
|
|
||||||
f.BoolVar(&keepPatchFiles, keepPatchFilesFlagName, keepPatchFiles, keepPatchFilesFlagUsage)
|
|
||||||
f.StringVarP(&filePath, "file", "f", "", "Install from a CR file")
|
f.StringVarP(&filePath, "file", "f", "", "Install from a CR file")
|
||||||
|
f.StringVarP(&opts.StorageClass, "storageClass", "s", "", "Storage class for qliksense")
|
||||||
f.BoolVarP(&pull, pullFlagName, pullFlagShorthand, pull, pullFlagUsage)
|
f.StringVarP(&opts.MongodbUri, "mongodbUri", "m", "", "mongodbUri for qliksense (i.e. mongodb://qlik-default-mongodb:27017/qliksense?ssl=false)")
|
||||||
f.BoolVarP(&push, pushFlagName, pushFlagShorthand, push, pushFlagUsage)
|
f.BoolVar(&opts.CleanPatchFiles, cleanPatchFilesFlagName, opts.CleanPatchFiles, cleanPatchFilesFlagUsage)
|
||||||
|
f.BoolVarP(&opts.Pull, pullFlagName, pullFlagShorthand, opts.Pull, pullFlagUsage)
|
||||||
|
f.BoolVarP(&opts.Push, pushFlagName, pushFlagShorthand, opts.Push, pushFlagUsage)
|
||||||
|
f.StringVarP(&opts.AcceptEULA, "acceptEULA", "a", opts.AcceptEULA, "Accept EULA for qliksense")
|
||||||
|
f.BoolVarP(&opts.DryRun, "dry-run", "", false, "Dry run will generate the patches without rotating keys")
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func validatePullPushFlagsOnInstall(q *qliksense.Qliksense, pull, push bool) error {
|
|
||||||
if pull && !push {
|
|
||||||
fmt.Printf("WARNING: pulling images without pushing them")
|
|
||||||
}
|
|
||||||
if push {
|
|
||||||
if err := ensureImageRegistrySetInCR(q); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
31
cmd/qliksense/keys.go
Normal file
31
cmd/qliksense/keys.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var keysCmd = &cobra.Command{
|
||||||
|
Use: "keys",
|
||||||
|
Short: "keys for qliksense",
|
||||||
|
}
|
||||||
|
|
||||||
|
func keysRotateCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
c := &cobra.Command{
|
||||||
|
Use: "rotate",
|
||||||
|
Short: "Rotate qliksense application keys",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := q.InstallQK8s("", &qliksense.InstallCommandOptions{
|
||||||
|
CleanPatchFiles: true,
|
||||||
|
RotateKeys: true,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
postFlightChecksCmd := AllPostflightChecks(q)
|
||||||
|
postFlightChecksCmd.DisableFlagParsing = true
|
||||||
|
return postFlightChecksCmd.Execute()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -20,69 +18,48 @@ func loadCrFile(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Long: `load a CR a file and create necessary structure for future use`,
|
Long: `load a CR a file and create necessary structure for future use`,
|
||||||
Example: `qliksense load -f file_name or cat cr_file | qliksense load -f -`,
|
Example: `qliksense load -f file_name or cat cr_file | qliksense load -f -`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return runLoadOrApplyCommandE(cmd, func(buffer []byte) error {
|
if crBytes, err := getCrBytesFromFileFlag(cmd); err != nil {
|
||||||
return q.LoadCr(bytes.NewReader(buffer), overwriteExistingContext)
|
return err
|
||||||
})
|
} else {
|
||||||
|
return q.LoadCr(crBytes, overwriteExistingContext)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
f := c.Flags()
|
f := c.Flags()
|
||||||
f.StringVarP(&filePath, "file", "f", "", "File to load CR from")
|
f.StringVarP(&filePath, "file", "f", "", "File to load CR from")
|
||||||
c.MarkFlagRequired("file")
|
|
||||||
f.BoolVarP(&overwriteExistingContext, "overwrite", "o", overwriteExistingContext, "Overwrite any existing contexts with the same name")
|
f.BoolVarP(&overwriteExistingContext, "overwrite", "o", overwriteExistingContext, "Overwrite any existing contexts with the same name")
|
||||||
|
|
||||||
eulaPreRunHooks.addValidator(fmt.Sprintf("%v %v", rootCommandName, c.Name()), loadOrApplyCommandEulaPreRunHook)
|
if err := c.MarkFlagRequired("file"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCrFileFromFlag(cmd *cobra.Command, flagName string) (*os.File, error) {
|
|
||||||
filePath := cmd.Flag(flagName).Value.String()
|
|
||||||
if filePath == "-" {
|
|
||||||
if !isInputFromPipe() {
|
|
||||||
return nil, errors.New("No input pipe present")
|
|
||||||
}
|
|
||||||
return os.Stdin, nil
|
|
||||||
}
|
|
||||||
file, e := os.Open(filePath)
|
|
||||||
if e != nil {
|
|
||||||
return nil, errors.Wrapf(e,
|
|
||||||
"unable to read the file %s", filePath)
|
|
||||||
}
|
|
||||||
return file, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isInputFromPipe() bool {
|
func isInputFromPipe() bool {
|
||||||
fileInfo, _ := os.Stdin.Stat()
|
fileInfo, _ := os.Stdin.Stat()
|
||||||
return fileInfo.Mode()&os.ModeCharDevice == 0
|
return fileInfo.Mode()&os.ModeCharDevice == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadOrApplyCommandEulaPreRunHook(cmd *cobra.Command, q *qliksense.Qliksense) (bool, error) {
|
func getCrFileFromFlag(cmd *cobra.Command, flagName string) (*os.File, error) {
|
||||||
file, err := getCrFileFromFlag(cmd, "file")
|
filePath := cmd.Flag(flagName).Value.String()
|
||||||
if err != nil {
|
if filePath == "-" {
|
||||||
return false, err
|
if !isInputFromPipe() {
|
||||||
}
|
return nil, errors.New("No input pipe present")
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
if crBytes, err := ioutil.ReadAll(file); err != nil {
|
|
||||||
return false, err
|
|
||||||
} else {
|
|
||||||
eulaPreRunHooks.addPostValidationArtifact("CR", crBytes)
|
|
||||||
return q.IsEulaAcceptedInCrFile(bytes.NewBuffer(crBytes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runLoadOrApplyCommandE(cmd *cobra.Command, callBack func(buffer []byte) error) error {
|
|
||||||
if crBytes := eulaPreRunHooks.getPostValidationArtifact("CR"); crBytes != nil {
|
|
||||||
return callBack(crBytes.([]byte))
|
|
||||||
} else {
|
|
||||||
file, err := getCrFileFromFlag(cmd, "file")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
if crBytes, err := ioutil.ReadAll(file); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
} else {
|
||||||
return callBack(crBytes)
|
return os.Stdin, nil
|
||||||
}
|
}
|
||||||
|
} else if file, err := os.Open(filePath); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to read the file %s", filePath)
|
||||||
|
} else {
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCrBytesFromFileFlag(cmd *cobra.Command) ([]byte, error) {
|
||||||
|
if file, err := getCrFileFromFlag(cmd, "file"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
defer file.Close()
|
||||||
|
return ioutil.ReadAll(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
96
cmd/qliksense/postflight.go
Normal file
96
cmd/qliksense/postflight.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
. "github.com/logrusorgru/aurora"
|
||||||
|
ansi "github.com/mattn/go-colorable"
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
postflight "github.com/qlik-oss/sense-installer/pkg/postflight"
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func postflightCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
postflightOpts := &postflight.PostflightOptions{}
|
||||||
|
var postflightCmd = &cobra.Command{
|
||||||
|
Use: "postflight",
|
||||||
|
Short: "perform postflight checks on the cluster",
|
||||||
|
Long: `perform postflight checks on the cluster`,
|
||||||
|
Example: `qliksense postflight <postflight_check_to_run>`,
|
||||||
|
}
|
||||||
|
f := postflightCmd.Flags()
|
||||||
|
f.BoolVarP(&postflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||||
|
return postflightCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func postflightMigrationCheck(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
out := ansi.NewColorableStdout()
|
||||||
|
postflightOpts := &postflight.PostflightOptions{}
|
||||||
|
var postflightMigrationCmd = &cobra.Command{
|
||||||
|
Use: "db-migration-check",
|
||||||
|
Short: "check mongodb migration status on the cluster",
|
||||||
|
Long: `check mongodb migration status on the cluster`,
|
||||||
|
Example: `qliksense postflight db-migration-check`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
pf := &postflight.QliksensePostflight{Q: q, P: postflightOpts, CG: &api.ClientGoUtils{Verbose: postflightOpts.Verbose}}
|
||||||
|
|
||||||
|
// Postflight db_migration_check
|
||||||
|
namespace, kubeConfigContents, err := pf.CG.LoadKubeConfigAndNamespace()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(out, "%s\n", Red("Postflight db_migration_check FAILED"))
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if namespace == "" {
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
if err = pf.DbMigrationCheck(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Fprintf(out, "%s\n", Red("Postflight db_migration_check FAILED"))
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fmt.Fprintf(out, "%s\n", Green("Postflight db_migration_check completed"))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
f := postflightMigrationCmd.Flags()
|
||||||
|
f.BoolVarP(&postflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||||
|
return postflightMigrationCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func AllPostflightChecks(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
out := ansi.NewColorableStdout()
|
||||||
|
postflightOpts := &postflight.PostflightOptions{}
|
||||||
|
var postflightAllChecksCmd = &cobra.Command{
|
||||||
|
Use: "all",
|
||||||
|
Short: "perform all checks",
|
||||||
|
Long: `perform all postflight checks`,
|
||||||
|
Example: `qliksense postflight all`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
pf := &postflight.QliksensePostflight{Q: q, P: postflightOpts, CG: &api.ClientGoUtils{Verbose: postflightOpts.Verbose}}
|
||||||
|
|
||||||
|
// run all postflight checks
|
||||||
|
fmt.Printf("Running all postflight checks...\n\n")
|
||||||
|
namespace, kubeConfigContents, err := pf.CG.LoadKubeConfigAndNamespace()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(out, "%s\n", Red("Unable to run all postflight checks"))
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if namespace == "" {
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
if err = pf.RunAllPostflightChecks(namespace, kubeConfigContents, postflightOpts); err != nil {
|
||||||
|
fmt.Fprintf(out, "%s\n", Red("1 or more preflight checks have FAILED"))
|
||||||
|
fmt.Printf("Completed running all postflight checks")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fmt.Fprintf(out, "%s\n", Green("All postflight checks have PASSED"))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
f := postflightAllChecksCmd.Flags()
|
||||||
|
f.BoolVarP(&postflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||||
|
return postflightAllChecksCmd
|
||||||
|
}
|
||||||
@@ -3,21 +3,27 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
ansi "github.com/mattn/go-colorable"
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/preflight"
|
|
||||||
. "github.com/logrusorgru/aurora"
|
. "github.com/logrusorgru/aurora"
|
||||||
|
ansi "github.com/mattn/go-colorable"
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/preflight"
|
||||||
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func preflightCmd(q *qliksense.Qliksense) *cobra.Command {
|
func preflightCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
preflightOpts := &preflight.PreflightOptions{
|
||||||
|
MongoOptions: &preflight.MongoOptions{},
|
||||||
|
}
|
||||||
var preflightCmd = &cobra.Command{
|
var preflightCmd = &cobra.Command{
|
||||||
Use: "preflight",
|
Use: "preflight",
|
||||||
Short: "perform preflight checks on the cluster",
|
Short: "perform preflight checks on the cluster",
|
||||||
Long: `perform preflight checks on the cluster`,
|
Long: `perform preflight checks on the cluster`,
|
||||||
Example: `qliksense preflight <preflight_check_to_run>`,
|
Example: `qliksense preflight <preflight_check_to_run>`,
|
||||||
}
|
}
|
||||||
|
f := preflightCmd.Flags()
|
||||||
|
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||||
return preflightCmd
|
return preflightCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,24 +38,24 @@ func pfDnsCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Long: `perform preflight dns check to check DNS connectivity status in the cluster`,
|
Long: `perform preflight dns check to check DNS connectivity status in the cluster`,
|
||||||
Example: `qliksense preflight dns`,
|
Example: `qliksense preflight dns`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
// Preflight DNS check
|
// Preflight DNS check
|
||||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight DNS check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if namespace == "" {
|
if namespace == "" {
|
||||||
namespace = "default"
|
namespace = "default"
|
||||||
}
|
}
|
||||||
if err = qp.CheckDns(namespace, kubeConfigContents); err != nil {
|
if err = qp.CheckDns(namespace, kubeConfigContents, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight DNS check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", Green("Preflight DNS check PASSED"))
|
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -65,26 +71,26 @@ func pfK8sVersionCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var preflightCheckK8sVersionCmd = &cobra.Command{
|
var preflightCheckK8sVersionCmd = &cobra.Command{
|
||||||
Use: "kube-version",
|
Use: "k8s-version",
|
||||||
Short: "check kubernetes version",
|
Short: "check kubernetes version",
|
||||||
Long: `check minimum valid kubernetes version on the cluster`,
|
Long: `check minimum valid kubernetes version on the cluster`,
|
||||||
Example: `qliksense preflight kube-version`,
|
Example: `qliksense preflight k8s-version`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
// Preflight Kubernetes minimum version check
|
// Preflight Kubernetes minimum version check
|
||||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight kubernetes minimum version check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err = qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
|
if err = qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight kubernetes minimum version check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", Green("Preflight kubernetes minimum version check PASSED"))
|
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -106,11 +112,11 @@ func pfAllChecksCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Long: `perform all preflight checks on the target cluster`,
|
Long: `perform all preflight checks on the target cluster`,
|
||||||
Example: `qliksense preflight all`,
|
Example: `qliksense preflight all`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
// Preflight run all checks
|
// Preflight run all checks
|
||||||
fmt.Printf("Running all preflight checks...\n\n")
|
fmt.Printf("Running all preflight checks...\n\n")
|
||||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Unable to run the preflight checks suite"))
|
fmt.Fprintf(out, "%s\n", Red("Unable to run the preflight checks suite"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
@@ -131,12 +137,7 @@ func pfAllChecksCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
f := preflightAllChecksCmd.Flags()
|
f := preflightAllChecksCmd.Flags()
|
||||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||||
f.StringVarP(&preflightOpts.MongoOptions.MongodbUrl, "mongodb-url", "", "", "mongodbUrl to try connecting to")
|
f.StringVarP(&preflightOpts.MongoOptions.MongodbUrl, "mongodb-url", "", "", "mongodbUrl to try connecting to")
|
||||||
f.StringVarP(&preflightOpts.MongoOptions.Username, "mongodb-username", "", "", "username to connect to mongodb")
|
|
||||||
f.StringVarP(&preflightOpts.MongoOptions.Password, "mongodb-password", "", "", "password to connect to mongodb")
|
|
||||||
f.StringVarP(&preflightOpts.MongoOptions.CaCertFile, "mongodb-ca-cert", "", "", "certificate to use for mongodb check")
|
f.StringVarP(&preflightOpts.MongoOptions.CaCertFile, "mongodb-ca-cert", "", "", "certificate to use for mongodb check")
|
||||||
f.StringVarP(&preflightOpts.MongoOptions.ClientCertFile, "mongodb-client-cert", "", "", "client-certificate to use for mongodb check")
|
|
||||||
f.BoolVar(&preflightOpts.MongoOptions.Tls, "mongodb-tls", false, "enable tls?")
|
|
||||||
|
|
||||||
return preflightAllChecksCmd
|
return preflightAllChecksCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,28 +148,28 @@ func pfDeploymentCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
}
|
}
|
||||||
var pfDeploymentCheckCmd = &cobra.Command{
|
var pfDeploymentCheckCmd = &cobra.Command{
|
||||||
Use: "deployment",
|
Use: "deployment",
|
||||||
Short: "perform preflight deploymwnt check",
|
Short: "perform preflight deployment check",
|
||||||
Long: `perform preflight deployment check to ensure that we can create deployments in the cluster`,
|
Long: `perform preflight deployment check to ensure that we can create deployments in the cluster`,
|
||||||
Example: `qliksense preflight deployment`,
|
Example: `qliksense preflight deployment`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
// Preflight deployments check
|
// Preflight deployments check
|
||||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if namespace == "" {
|
if namespace == "" {
|
||||||
namespace = "default"
|
namespace = "default"
|
||||||
}
|
}
|
||||||
if err = qp.CheckDeployment(namespace, kubeConfigContents); err != nil {
|
if err = qp.CheckDeployment(namespace, kubeConfigContents, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", Green("Preflight deployment check PASSED"))
|
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -189,12 +190,12 @@ func pfServiceCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Long: `perform preflight service check to ensure that we are able to create services in the cluster`,
|
Long: `perform preflight service check to ensure that we are able to create services in the cluster`,
|
||||||
Example: `qliksense preflight service`,
|
Example: `qliksense preflight service`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
// Preflight service check
|
// Preflight service check
|
||||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -202,12 +203,12 @@ func pfServiceCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
if namespace == "" {
|
if namespace == "" {
|
||||||
namespace = "default"
|
namespace = "default"
|
||||||
}
|
}
|
||||||
if err = qp.CheckService(namespace, kubeConfigContents); err != nil {
|
if err = qp.CheckService(namespace, kubeConfigContents, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", Green("Preflight service check PASSED"))
|
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -228,24 +229,24 @@ func pfPodCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Long: `perform preflight pod check to ensure we can create pods in the cluster`,
|
Long: `perform preflight pod check to ensure we can create pods in the cluster`,
|
||||||
Example: `qliksense preflight pod`,
|
Example: `qliksense preflight pod`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
// Preflight pod check
|
// Preflight pod check
|
||||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if namespace == "" {
|
if namespace == "" {
|
||||||
namespace = "default"
|
namespace = "default"
|
||||||
}
|
}
|
||||||
if err = qp.CheckPod(namespace, kubeConfigContents); err != nil {
|
if err = qp.CheckPod(namespace, kubeConfigContents, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", Green("Preflight pod check PASSED"))
|
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -266,21 +267,21 @@ func pfCreateRoleCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Long: `perform preflight role check to ensure we are able to create a role in the cluster`,
|
Long: `perform preflight role check to ensure we are able to create a role in the cluster`,
|
||||||
Example: `qliksense preflight createRole`,
|
Example: `qliksense preflight createRole`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
// Preflight role check
|
// Preflight role check
|
||||||
namespace, _, err := preflight.InitPreflight()
|
namespace, _, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err = qp.CheckCreateRole(namespace); err != nil {
|
if err = qp.CheckCreateRole(namespace, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", Green("Preflight role check PASSED"))
|
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -301,21 +302,21 @@ func pfCreateRoleBindingCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Long: `perform preflight rolebinding check to ensure we are able to create a rolebinding in the cluster`,
|
Long: `perform preflight rolebinding check to ensure we are able to create a rolebinding in the cluster`,
|
||||||
Example: `qliksense preflight rolebinding`,
|
Example: `qliksense preflight rolebinding`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
// Preflight createRoleBinding check
|
// Preflight createRoleBinding check
|
||||||
namespace, _, err := preflight.InitPreflight()
|
namespace, _, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight rolebinding check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err = qp.CheckCreateRoleBinding(namespace); err != nil {
|
if err = qp.CheckCreateRoleBinding(namespace, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight rolebinding check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", Green("Preflight rolebinding check PASSED"))
|
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -332,25 +333,25 @@ func pfCreateServiceAccountCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
|
|
||||||
var preflightServiceAccountCmd = &cobra.Command{
|
var preflightServiceAccountCmd = &cobra.Command{
|
||||||
Use: "serviceaccount",
|
Use: "serviceaccount",
|
||||||
Short: "preflight create ServiceAccount check",
|
Short: "preflight create serviceaccount check",
|
||||||
Long: `perform preflight serviceaccount check to ensure we are able to create a service account in the cluster`,
|
Long: `perform preflight serviceaccount check to ensure we are able to create a service account in the cluster`,
|
||||||
Example: `qliksense preflight serviceaccount`,
|
Example: `qliksense preflight serviceaccount`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
// Preflight createServiceAccount check
|
// Preflight createServiceAccount check
|
||||||
namespace, _, err := preflight.InitPreflight()
|
namespace, _, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight ServiceAccount check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err = qp.CheckCreateServiceAccount(namespace); err != nil {
|
if err = qp.CheckCreateServiceAccount(namespace, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight ServiceAccount check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", Green("Preflight ServiceAccount check PASSED"))
|
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -370,21 +371,21 @@ func pfCreateAuthCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Long: `perform preflight authcheck that combines the role, rolebinding and serviceaccount checks`,
|
Long: `perform preflight authcheck that combines the role, rolebinding and serviceaccount checks`,
|
||||||
Example: `qliksense preflight authcheck`,
|
Example: `qliksense preflight authcheck`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
// Preflight authcheck
|
// Preflight authcheck
|
||||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight authcheck FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err = qp.CheckCreateRB(namespace, kubeConfigContents); err != nil {
|
if err = qp.CheckCreateRB(namespace, kubeConfigContents); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight authcheck FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", Green("Preflight authcheck PASSED"))
|
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -405,34 +406,108 @@ func pfMongoCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Long: `perform preflight mongo check to ensure we are able to connect to a mongodb instance in the cluster`,
|
Long: `perform preflight mongo check to ensure we are able to connect to a mongodb instance in the cluster`,
|
||||||
Example: `qliksense preflight mongo OR preflight mongo --url=<url>`,
|
Example: `qliksense preflight mongo OR preflight mongo --url=<url>`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts}
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
// Preflight mongo check
|
// Preflight mongo check
|
||||||
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight mongo check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if namespace == "" {
|
if namespace == "" {
|
||||||
namespace = "default"
|
namespace = "default"
|
||||||
}
|
}
|
||||||
if err = qp.CheckMongo(kubeConfigContents, namespace, preflightOpts); err != nil {
|
if err = qp.CheckMongo(kubeConfigContents, namespace, preflightOpts, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight mongo check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n", err)
|
fmt.Printf("Error: %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "%s\n", Green("Preflight mongo check PASSED"))
|
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
f := preflightMongoCmd.Flags()
|
f := preflightMongoCmd.Flags()
|
||||||
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||||
f.StringVarP(&preflightOpts.MongoOptions.MongodbUrl, "url", "", "", "mongodbUrl to try connecting to")
|
f.StringVarP(&preflightOpts.MongoOptions.MongodbUrl, "url", "", "", "mongodbUrl to try connecting to")
|
||||||
f.StringVarP(&preflightOpts.MongoOptions.Username, "username", "", "", "username to connect to mongodb")
|
|
||||||
f.StringVarP(&preflightOpts.MongoOptions.Password, "password", "", "", "password to connect to mongodb")
|
|
||||||
f.StringVarP(&preflightOpts.MongoOptions.CaCertFile, "ca-cert", "", "", "ca certificate to use for mongodb check")
|
f.StringVarP(&preflightOpts.MongoOptions.CaCertFile, "ca-cert", "", "", "ca certificate to use for mongodb check")
|
||||||
f.StringVarP(&preflightOpts.MongoOptions.ClientCertFile, "client-cert", "", "", "client-certificate to use for mongodb check")
|
|
||||||
f.BoolVar(&preflightOpts.MongoOptions.Tls, "tls", false, "enable tls?")
|
|
||||||
return preflightMongoCmd
|
return preflightMongoCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pfCleanupCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
out := ansi.NewColorableStdout()
|
||||||
|
preflightOpts := &preflight.PreflightOptions{
|
||||||
|
MongoOptions: &preflight.MongoOptions{},
|
||||||
|
}
|
||||||
|
|
||||||
|
var pfCleanCmd = &cobra.Command{
|
||||||
|
Use: "clean",
|
||||||
|
Short: "perform preflight clean",
|
||||||
|
Long: `perform preflight clean to ensure that all resources are cleared up in the cluster`,
|
||||||
|
Example: `qliksense preflight clean`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
|
// Preflight clean
|
||||||
|
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(out, "%s\n", Red("Preflight cleanup FAILED"))
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
if err = qp.Cleanup(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Fprintf(out, "%s\n", Red("Preflight cleanup FAILED"))
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fmt.Fprintf(out, "%s\n", Green("Preflight cleanup complete"))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
f := pfCleanCmd.Flags()
|
||||||
|
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||||
|
return pfCleanCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func pfVerifyCAChainCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
out := ansi.NewColorableStdout()
|
||||||
|
preflightOpts := &preflight.PreflightOptions{
|
||||||
|
MongoOptions: &preflight.MongoOptions{},
|
||||||
|
}
|
||||||
|
|
||||||
|
var pfVerifyCAChainCmd = &cobra.Command{
|
||||||
|
Use: "verify-ca-chain",
|
||||||
|
Short: "verify-ca-chain using openssl verify",
|
||||||
|
Long: `verify the CA chain using openssl verify to ensure that mongodb certificate is valid`,
|
||||||
|
Example: `qliksense preflight verify-ca-chain`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
qp := &preflight.QliksensePreflight{Q: q, P: preflightOpts, CG: &api.ClientGoUtils{Verbose: preflightOpts.Verbose}}
|
||||||
|
|
||||||
|
// Preflight service check
|
||||||
|
namespace, kubeConfigContents, err := qp.CG.LoadKubeConfigAndNamespace()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if namespace == "" {
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
if err = qp.VerifyCAChain(kubeConfigContents, namespace, preflightOpts, false); err != nil {
|
||||||
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fmt.Fprintf(out, "%s\n", Green("PASSED"))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
f := pfVerifyCAChainCmd.Flags()
|
||||||
|
f.BoolVarP(&preflightOpts.Verbose, "verbose", "v", false, "verbose mode")
|
||||||
|
return pfVerifyCAChainCmd
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -34,22 +31,8 @@ func pushQliksenseImages(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Short: "Push docker images for offline install",
|
Short: "Push docker images for offline install",
|
||||||
Example: `qliksense push`,
|
Example: `qliksense push`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if err := ensureImageRegistrySetInCR(q); err != nil {
|
return q.PushImagesForCurrentCR()
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
return q.PushImagesForCurrentCR()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureImageRegistrySetInCR(q *qliksense.Qliksense) error {
|
|
||||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
|
||||||
if qcr, err := qConfig.GetCurrentCR(); err != nil {
|
|
||||||
return err
|
|
||||||
} else if registry := qcr.Spec.GetImageRegistry(); registry == "" {
|
|
||||||
return errors.New("no image registry set in the CR; to set it use: qliksense config set-image-registry")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
. "github.com/logrusorgru/aurora"
|
||||||
ansi "github.com/mattn/go-colorable"
|
ansi "github.com/mattn/go-colorable"
|
||||||
"github.com/mitchellh/go-homedir"
|
"github.com/mitchellh/go-homedir"
|
||||||
"github.com/qlik-oss/sense-installer/pkg"
|
"github.com/qlik-oss/sense-installer/pkg"
|
||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
. "github.com/logrusorgru/aurora"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// To run this project in debug mode, run:
|
// To run this project in debug mode, run:
|
||||||
@@ -23,17 +22,17 @@ import (
|
|||||||
// qliksense <command>
|
// qliksense <command>
|
||||||
|
|
||||||
const (
|
const (
|
||||||
qlikSenseHomeVar = "QLIKSENSE_HOME"
|
qlikSenseHomeVar = "QLIKSENSE_HOME"
|
||||||
qlikSenseDirVar = ".qliksense"
|
qlikSenseDirVar = ".qliksense"
|
||||||
keepPatchFilesFlagName = "keep-config-repo-patches"
|
cleanPatchFilesFlagName = "clean"
|
||||||
keepPatchFilesFlagUsage = "Keep config repo patch files (for debugging)"
|
cleanPatchFilesFlagUsage = "Set --clean=false to keep any prior config repo file changes on install (for debugging)"
|
||||||
pullFlagName = "pull"
|
pullFlagName = "pull"
|
||||||
pullFlagShorthand = "d"
|
pullFlagShorthand = "d"
|
||||||
pullFlagUsage = "If using private docker registry, pull (download) all required Qliksense images before install"
|
pullFlagUsage = "If using private docker registry, pull (download) all required qliksense images before install"
|
||||||
pushFlagName = "push"
|
pushFlagName = "push"
|
||||||
pushFlagShorthand = "u"
|
pushFlagShorthand = "u"
|
||||||
pushFlagUsage = "If using private docker registry, push (upload) all downloaded Qliksense images to that registry before install"
|
pushFlagUsage = "If using private docker registry, push (upload) all downloaded qliksense images to that registry before install"
|
||||||
rootCommandName = "qliksense"
|
rootCommandName = "qliksense"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initAndExecute() error {
|
func initAndExecute() error {
|
||||||
@@ -46,7 +45,7 @@ func initAndExecute() error {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
// create dirs and appropriate files for setting up contexts
|
// create dirs and appropriate files for setting up contexts
|
||||||
api.LogDebugMessage("QliksenseHomeDir: %s", qlikSenseHome)
|
api.LogDebugMessage("QliksenseHomeDir: %s\n", qlikSenseHome)
|
||||||
|
|
||||||
qliksenseClient := qliksense.New(qlikSenseHome)
|
qliksenseClient := qliksense.New(qlikSenseHome)
|
||||||
cmd := rootCmd(qliksenseClient)
|
cmd := rootCmd(qliksenseClient)
|
||||||
@@ -101,12 +100,11 @@ func commandUsesContext(commandName string) bool {
|
|||||||
func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
|
func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: rootCommandName,
|
Use: rootCommandName,
|
||||||
Short: "Qliksense cli tool",
|
Short: "qliksense cli tool",
|
||||||
Long: `qliksense cli tool provides functionality to perform operations on qliksense-k8s, qliksense operator, and kubernetes cluster`,
|
Long: `qliksense cli tool provides functionality to perform operations on qliksense-k8s, qliksense operator, and kubernetes cluster`,
|
||||||
Args: cobra.ArbitraryArgs,
|
Args: cobra.ArbitraryArgs,
|
||||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
if commandUsesContext(cmd.CommandPath()) {
|
if commandUsesContext(cmd.CommandPath()) {
|
||||||
globalEulaPreRun(cmd, p)
|
|
||||||
if err := p.SetUpQliksenseDefaultContext(); err != nil {
|
if err := p.SetUpQliksenseDefaultContext(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -114,24 +112,10 @@ func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
|
|||||||
if err := pf.Initialize(); err != nil {
|
if err := pf.Initialize(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
globalEulaPostRun(cmd, p)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
|
||||||
if commandUsesContext(cmd.CommandPath()) {
|
|
||||||
globalEulaPostRun(cmd, p)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
SilenceUsage: true,
|
||||||
}
|
}
|
||||||
origHelpFunc := cmd.HelpFunc()
|
|
||||||
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
|
|
||||||
if !commandUsesContext(cmd.CommandPath()) {
|
|
||||||
cmd.Flags().MarkHidden("acceptEULA")
|
|
||||||
}
|
|
||||||
origHelpFunc(cmd, args)
|
|
||||||
})
|
|
||||||
accept := ""
|
|
||||||
cmd.PersistentFlags().StringVarP(&accept, "acceptEULA", "a", "", "Accept EULA for qliksense")
|
|
||||||
cmd.Flags().SetInterspersed(false)
|
cmd.Flags().SetInterspersed(false)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@@ -167,7 +151,9 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
|||||||
// add config command
|
// add config command
|
||||||
configCmd := configCmd(p)
|
configCmd := configCmd(p)
|
||||||
cmd.AddCommand(configCmd)
|
cmd.AddCommand(configCmd)
|
||||||
|
/** disabling for now
|
||||||
configCmd.AddCommand(configApplyCmd(p))
|
configCmd.AddCommand(configApplyCmd(p))
|
||||||
|
**/
|
||||||
configCmd.AddCommand(configViewCmd(p))
|
configCmd.AddCommand(configViewCmd(p))
|
||||||
|
|
||||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||||
@@ -196,21 +182,21 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
|||||||
// add clean-config-repo-patches command as a sub-command to the app config sub-command
|
// add clean-config-repo-patches command as a sub-command to the app config sub-command
|
||||||
configCmd.AddCommand(cleanConfigRepoPatchesCmd(p))
|
configCmd.AddCommand(cleanConfigRepoPatchesCmd(p))
|
||||||
|
|
||||||
|
|
||||||
// open editor for config
|
// open editor for config
|
||||||
configCmd.AddCommand(configEditCmd(p))
|
configCmd.AddCommand(configEditCmd(p))
|
||||||
|
|
||||||
|
// add unset for config
|
||||||
|
configCmd.AddCommand((unsetCmd(p)))
|
||||||
|
|
||||||
// add uninstall command
|
// add uninstall command
|
||||||
cmd.AddCommand(uninstallCmd(p))
|
cmd.AddCommand(uninstallCmd(p))
|
||||||
|
|
||||||
// add export command
|
|
||||||
cmd.AddCommand(exportCmd(p))
|
|
||||||
|
|
||||||
// add crds
|
// add crds
|
||||||
cmd.AddCommand(crdsCmd)
|
cmd.AddCommand(crdsCmd)
|
||||||
crdsCmd.AddCommand(crdsViewCmd(p))
|
crdsCmd.AddCommand(crdsViewCmd(p))
|
||||||
crdsCmd.AddCommand(crdsInstallCmd(p))
|
crdsCmd.AddCommand(crdsInstallCmd(p))
|
||||||
|
|
||||||
// add preflight command
|
// add preflight commands
|
||||||
preflightCmd := preflightCmd(p)
|
preflightCmd := preflightCmd(p)
|
||||||
preflightCmd.AddCommand(pfDnsCheckCmd(p))
|
preflightCmd.AddCommand(pfDnsCheckCmd(p))
|
||||||
preflightCmd.AddCommand(pfK8sVersionCheckCmd(p))
|
preflightCmd.AddCommand(pfK8sVersionCheckCmd(p))
|
||||||
@@ -223,41 +209,26 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
|||||||
preflightCmd.AddCommand(pfCreateRoleBindingCheckCmd(p))
|
preflightCmd.AddCommand(pfCreateRoleBindingCheckCmd(p))
|
||||||
preflightCmd.AddCommand(pfCreateServiceAccountCheckCmd(p))
|
preflightCmd.AddCommand(pfCreateServiceAccountCheckCmd(p))
|
||||||
preflightCmd.AddCommand(pfCreateAuthCheckCmd(p))
|
preflightCmd.AddCommand(pfCreateAuthCheckCmd(p))
|
||||||
|
preflightCmd.AddCommand(pfVerifyCAChainCmd(p))
|
||||||
|
preflightCmd.AddCommand(pfCleanupCmd(p))
|
||||||
|
|
||||||
cmd.AddCommand(preflightCmd)
|
cmd.AddCommand(preflightCmd)
|
||||||
cmd.AddCommand(loadCrFile(p))
|
cmd.AddCommand(loadCrFile(p))
|
||||||
cmd.AddCommand((applyCmd(p)))
|
cmd.AddCommand((applyCmd(p)))
|
||||||
|
|
||||||
|
// add postflight command
|
||||||
|
postflightCmd := postflightCmd(p)
|
||||||
|
postflightCmd.AddCommand(postflightMigrationCheck(p))
|
||||||
|
postflightCmd.AddCommand(AllPostflightChecks(p))
|
||||||
|
|
||||||
|
cmd.AddCommand(postflightCmd)
|
||||||
|
|
||||||
|
// add keys command
|
||||||
|
cmd.AddCommand(keysCmd)
|
||||||
|
keysCmd.AddCommand(keysRotateCmd(p))
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func copy(src, dst string) (int64, error) {
|
|
||||||
var (
|
|
||||||
source, destination *os.File
|
|
||||||
sourceFileStat os.FileInfo
|
|
||||||
err error
|
|
||||||
nBytes int64
|
|
||||||
)
|
|
||||||
if sourceFileStat, err = os.Stat(src); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sourceFileStat.Mode().IsRegular() {
|
|
||||||
return 0, fmt.Errorf("%s is not a regular file", src)
|
|
||||||
}
|
|
||||||
|
|
||||||
if source, err = os.Open(src); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer source.Close()
|
|
||||||
|
|
||||||
if destination, err = os.Create(dst); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer destination.Close()
|
|
||||||
nBytes, err = io.Copy(destination, source)
|
|
||||||
return nBytes, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func levenstein(cmd *cobra.Command) {
|
func levenstein(cmd *cobra.Command) {
|
||||||
cmd.SuggestionsMinimumDistance = 2
|
cmd.SuggestionsMinimumDistance = 2
|
||||||
if len(os.Args) > 1 {
|
if len(os.Args) > 1 {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Preflight checks provide pre-installation cluster conformance testing and validation before we install qliksense on the cluster. We gather a suite of conformance tests that can be easily written and run on the target cluster to verify that cluster-specific requirements are met.
|
Preflight checks provide pre-installation cluster conformance testing and validation before we install qliksense on the cluster. We gather a suite of conformance tests that can be easily written and run on the target cluster to verify that cluster-specific requirements are met.
|
||||||
|
|
||||||
We support the following tests at the moment as part of preflight checks, and the range of the suite will be expanded in future.
|
We support a couple of tests at the moment as part of preflight checks, and the range of the suite will be expanded in future.
|
||||||
|
|
||||||
Run the following command to view help about the commands supported by preflight at any moment:
|
Run the following command to view help about the commands supported by preflight at any moment:
|
||||||
```
|
```
|
||||||
@@ -23,6 +23,20 @@ Run the following command to execute a specific check
|
|||||||
qliksense preflight dns
|
qliksense preflight dns
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Running cleanup
|
||||||
|
Run the following command to cleanup entities created for preflight checks that were left behind on the cluster.
|
||||||
|
```
|
||||||
|
qliksense preflight clean
|
||||||
|
```
|
||||||
|
|
||||||
|
### qliksense postflight
|
||||||
|
Postflight checks are performed after qliksense is installed on the cluster and during normal operating mode of the product. Such checks can range from validating certain conditions to checking the status of certain operations or entities.
|
||||||
|
|
||||||
|
Run the following command to view help about the commands supported by postflight at any moment:
|
||||||
|
```
|
||||||
|
qliksense postflight
|
||||||
|
```
|
||||||
|
|
||||||
### qliksense load
|
### qliksense load
|
||||||
|
|
||||||
`qliksense load` command takes input from a file or from pipe
|
`qliksense load` command takes input from a file or from pipe
|
||||||
@@ -57,10 +71,9 @@ spec:
|
|||||||
value: "yes"
|
value: "yes"
|
||||||
secrets:
|
secrets:
|
||||||
qliksense:
|
qliksense:
|
||||||
- name: mongoDbUri
|
- name: mongodbUri
|
||||||
value: mongodb://qlik-test-mongodb:27017/qliksense?ssl=false
|
value: mongodb://qlik-test-mongodb:27017/qliksense?ssl=false
|
||||||
profile: docker-desktop
|
profile: docker-desktop
|
||||||
rotateKeys: "yes"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`qliksense apply` does everything `qliksense load` does but will install Qlik Sense into the cluster as well
|
`qliksense apply` does everything `qliksense load` does but will install Qlik Sense into the cluster as well
|
||||||
@@ -98,7 +111,7 @@ spec:
|
|||||||
value: "yes"
|
value: "yes"
|
||||||
secrets:
|
secrets:
|
||||||
qliksense:
|
qliksense:
|
||||||
- name: mongoDbUri
|
- name: mongodbUri
|
||||||
value: "mongo://mongo:3307"
|
value: "mongo://mongo:3307"
|
||||||
- name: messagingPassword
|
- name: messagingPassword
|
||||||
valueFromKey: messagingPassword
|
valueFromKey: messagingPassword
|
||||||
@@ -116,7 +129,6 @@ In this case, the result of `qliksense about` command would display information
|
|||||||
|
|
||||||
It supports the following flags:
|
It supports the following flags:
|
||||||
|
|
||||||
- `qliksense config apply` - generate the patches and apply manifests to K8s
|
|
||||||
- `qliksense config list-contexts` - get and list contexts
|
- `qliksense config list-contexts` - get and list contexts
|
||||||
- `qliksense config set` - configure a key-value pair into the current context
|
- `qliksense config set` - configure a key-value pair into the current context
|
||||||
- `qliksense config set-configs` - set configurations into qliksense context as key-value pairs
|
- `qliksense config set-configs` - set configurations into qliksense context as key-value pairs
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# How CLI works
|
# How CLI works
|
||||||
|
|
||||||
At the initialization, `qliksense` cli creates few files in the director `~/.qliksene` and it contains following files:
|
At the initialization, `qliksense` cli creates few files in the director `~/.qliksense` and it contains following files:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
.qliksense
|
.qliksense
|
||||||
@@ -23,9 +23,8 @@ spec:
|
|||||||
profile: docker-desktop
|
profile: docker-desktop
|
||||||
secrets:
|
secrets:
|
||||||
qliksense:
|
qliksense:
|
||||||
- name: mongoDbUri
|
- name: mongodbUri
|
||||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||||
rotateKeys: "yes"
|
|
||||||
releaseName: qlik-default
|
releaseName: qlik-default
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -50,7 +49,7 @@ In this mode `qliksense` CLI downloads the specified version from [qliksense-k8s
|
|||||||
|
|
||||||
The qliksense cli creates a CR for the QlikSense operator and all config operations are performed to edit the CR.
|
The qliksense cli creates a CR for the QlikSense operator and all config operations are performed to edit the CR.
|
||||||
|
|
||||||
`qliksense install` or `qliksense config apply` will generate patches in local file system (i.e `~/.qliksense/contexts/<context-name>/qlik-k8s`) and
|
`qliksense install` will generate patches in local file system (i.e `~/.qliksense/contexts/<context-name>/qlik-k8s`) and
|
||||||
|
|
||||||
- Install those manifests into the cluster
|
- Install those manifests into the cluster
|
||||||
- Create a custom resource (CR) for the `qliksene operator`.
|
- Create a custom resource (CR) for the `qliksene operator`.
|
||||||
@@ -68,7 +67,7 @@ qliksense config set git.repository="https://github.com/my-org/qliksense-k8s"
|
|||||||
qliksense config set git.accessToken="<mySecretToken>"
|
qliksense config set git.accessToken="<mySecretToken>"
|
||||||
```
|
```
|
||||||
|
|
||||||
When you perform `qliksense install` or `qliksene config apply`, qliksense operator performs these tasks:
|
When you perform `qliksense install`, qliksense operator performs these tasks:
|
||||||
|
|
||||||
- Download corresponding version of manifests from the your git repo
|
- Download corresponding version of manifests from the your git repo
|
||||||
- Generate kustomize patches
|
- Generate kustomize patches
|
||||||
|
|||||||
@@ -1,53 +1,162 @@
|
|||||||
# Getting started
|
# Getting started
|
||||||
|
|
||||||
|
To get familiar with the Qlik Sense on Kubernetes Operator Command Line Interface (CLI), we will install Qlik Sense on Kubernetes on docker desktop. In subsequent sections we will enhance this configuration to include an Identity Provider (keycloak) and demonstrate air gapped capabilities as well.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Kubernetes cluster (Docker Desktop with enabled Kubernetes)
|
- Kubernetes cluster (Docker Desktop with enabled Kubernetes)
|
||||||
- `kubectl` installed, configured and able to communicate with kubernetes cluster. _`qliksense` CLI uses `kubectl` under the hood to perform operations on cluster_
|
- `kubectl` installed, configured and able to communicate with kubernetes cluster. _`qliksense` CLI uses `kubectl` to perform some operations on cluster_
|
||||||
|
|
||||||
## Installing `qliksense` CLI
|
## Installing `qliksense` CLI
|
||||||
|
|
||||||
Download the executable for your platform from [releases page](https://github.com/qlik-oss/sense-installer/releases) and rename it to `qliksense`
|
Download the executable for your platform from [releases page](https://github.com/qlik-oss/sense-installer/releases) and rename it to `qliksense`
|
||||||
|
|
||||||
??? tldr "Linux"
|
=== "Linux"
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
curl -Lo qliksense https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-linux-amd64
|
# bash
|
||||||
chmod +x qliksense
|
|
||||||
sudo mv qliksense /usr/local/bin
|
curl -LOJ https://storage.googleapis.com/kubernetes-release/release/v1.16.8/bin/linux/amd64/kubectl
|
||||||
|
curl -LOJ https://github.com/qlik-oss/sense-installer/releases/latest/download/qliksense-linux-amd64
|
||||||
|
sudo mv qliksense-linux-amd64 kubectl /usr/local/bin
|
||||||
|
sudo chmod ugo+x /usr/local/bin/qliksense-linux-amd64 /usr/local/bin/kubectl
|
||||||
|
sudo ln -s /usr/local/bin/qliksense-linux-amd64 /usr/local/bin/qliksense
|
||||||
|
sudo ln -s /usr/local/bin/qliksense-linux-amd64 /usr/local/bin/kubectl-qliksense
|
||||||
```
|
```
|
||||||
|
|
||||||
??? tldr "MacOS"
|
=== "MacOS"
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
curl -Lo qliksense https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-darwin-amd64
|
# bash
|
||||||
chmod +x qliksense
|
|
||||||
sudo mv qliksense /usr/local/bin
|
curl -LOJ https://storage.googleapis.com/kubernetes-release/release/v1.16.8/bin/darwin/amd64/kubectl
|
||||||
|
curl -LOJ https://github.com/qlik-oss/sense-installer/releases/latest/download/qliksense-darwin-amd64
|
||||||
|
sudo mv qliksense-darwin-amd64 kubectl /usr/local/bin
|
||||||
|
sudo chmod ugo+x /usr/local/bin/qliksense-darwin-amd64 /usr/local/bin/kubectl
|
||||||
|
sudo ln -s /usr/local/bin/qliksense-darwin-amd64 /usr/local/bin/qliksense
|
||||||
|
sudo ln -s /usr/local/bin/qliksense-darwin-amd64 /usr/local/bin/kubectl-qliksense
|
||||||
```
|
```
|
||||||
|
|
||||||
??? tldr "Windows"
|
=== "Windows"
|
||||||
Download Windows executable and add it in your `PATH` as `qliksense.exe`
|
|
||||||
|
|
||||||
[https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-windows-amd64.exe](https://github.com/qlik-oss/sense-installer/releases/download/v0.7.0/qliksense-windows-amd64.exe)
|
|
||||||
|
|
||||||
|
``` powershell
|
||||||
|
# powershell
|
||||||
|
|
||||||
|
Invoke-WebRequest https://storage.googleapis.com/kubernetes-release/release/v1.16.8/bin/windows/amd64/kubectl.exe -O C:\bin\kubectl.exe
|
||||||
|
Invoke-WebRequest https://github.com/qlik-oss/sense-installer/releases/latest/download/qliksense-windows-amd64.exe -O C:\bin\qliksense.exe
|
||||||
|
Copy-Item C:\bin\qliksense.exe C:\bin\kubectl-qliksense.exe
|
||||||
|
# Add C:\bin to current Path
|
||||||
|
$Env:Path += ";C:\bin"
|
||||||
|
# Save Path to User environment scope
|
||||||
|
[Environment]::SetEnvironmentVariable("Path",[Environment]::GetEnvironmentVariable("Path", [EnvironmentVariableTarget]::User) + ";C:\bin",[EnvironmentVariableTarget]::User)
|
||||||
|
```
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
- To download the version `v0.0.2` from qliksense-k8s [releases](https://github.com/qlik-oss/qliksense-k8s/releases).
|
### Setting the contexts
|
||||||
|
|
||||||
|
By default a `qlik-default` configuration context is provided and can be used, as is. In effect, this is the name of the Qlik Sense instance in the target cluster. All resources installed into the target namespace will be prefixed with `qlik-default`. The name of the Qlik Sense application will correspondingly be `qliksense`.
|
||||||
|
|
||||||
|
Ex.: To change this to `qliksense-dev`:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
qliksense fetch v0.0.2
|
qliksense config set-context qliksense-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
- To install CRDs for QSEoK and qliksense operator into the kubernetes cluster.
|
!!! info ""
|
||||||
|
For the purposes of the Quick Start we will be using `qlik-default`
|
||||||
|
|
||||||
|
The target namespace is determined by the kubectl connection context.
|
||||||
|
|
||||||
|
ex. Ensure a connection to cluster to change the configuration context's target namespace with kubectl to `qliksense`
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
qliksense crds install --all
|
kubectl config set-context --current --namespace=qliksense
|
||||||
```
|
```
|
||||||
|
|
||||||
- To install QSEoK into a namespace in the kubernetes cluster where `kubectl` is pointing to.
|
!!! info ""
|
||||||
|
For the purposes of the Quick Start we will be using the default namespace. (`default`)
|
||||||
|
|
||||||
|
### Downloading a version of Qlik Sense on Kubernetes
|
||||||
|
|
||||||
|
To download the latest version of Qlik Sense on Kubernetes from qliksense-k8s
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
qliksense install --acceptEULA="yes"
|
qliksense fetch
|
||||||
|
```
|
||||||
|
|
||||||
|
#### More Options
|
||||||
|
|
||||||
|
- To download a specific version `v1.59.20` from qliksense-k8s [releases](https://github.com/qlik-oss/qliksense-k8s/releases)
|
||||||
|
```shell
|
||||||
|
qliksense fetch v1.58.20
|
||||||
|
```
|
||||||
|
- To download from a GitHub repository fork of the `qliksense-k8s` repository (master branch)
|
||||||
|
```shell
|
||||||
|
qliksense fetch --url https://github.com/bkuschel/qliksense-k8s.git master
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deployment Profiles
|
||||||
|
|
||||||
|
Deployment profiles are a sets components that require sets of key/value pairs to satisfy the requirements for the generation of a Qlik Sense on Kubernetes manifest. Along with the profile name, sets of key/value pairs are provided through the Qlik Sense custom application resources (see here).
|
||||||
|
|
||||||
|
Profiles can be developed and added to the qliksense-k8s repo but is considered an advanced topic (see here) not covered here.
|
||||||
|
|
||||||
|
#### Default Profile: Docker Desktop
|
||||||
|
|
||||||
|
By default, the `docker-desktop` profile is associated with the configuration context when initially created. This profile is guaranteed to work on Docker Desktop but can generally be used on other types of Kubernetes clusters, provided that the required configuration tweaks are provided specific to the hosting requirements (Ex. storage class).
|
||||||
|
|
||||||
|
The docker-desktop profile does not have any scaling characteristics and is generally set up to have the ability to work on a reasonably powerful computer (16GB, 4 cores minimum, greater is better). It also includes a self-contained mongodb instance for non-production purposes.
|
||||||
|
|
||||||
|
Generally it doesn't require any extra configuration to work except an acceptance of the Qlik User License Agreement (QULA), which is prompted on install but can also be set in advance (having read the QULA)
|
||||||
|
|
||||||
|
```shell
|
||||||
|
qliksense config set-configs qliksense.acceptEULA="yes"
|
||||||
|
```
|
||||||
|
|
||||||
|
More information on the possible configuration parameters for docker-desktop here (see here).
|
||||||
|
|
||||||
|
!!! Info
|
||||||
|
To access an installation of the docker desktop profile in docker desktop, the host `elastic.example` needs to be added to the system host file as an alias to `127.0.0.1`
|
||||||
|
|
||||||
|
```
|
||||||
|
127.0.0.1 elastic.example
|
||||||
|
```
|
||||||
|
|
||||||
|
File location:
|
||||||
|
|
||||||
|
- Linux - `/etc/hosts`
|
||||||
|
- MacOS - `/etc/hosts`
|
||||||
|
- Windows - `C:\Windows\System32\drivers\etc\hosts`
|
||||||
|
|
||||||
|
### Installing Qlik Sense on Kubernetes
|
||||||
|
|
||||||
|
#### Custom Resource Definitions (CRDs)
|
||||||
|
|
||||||
|
Besides the CLI, a Kubernetes operator (read here) is a core component of the Qlik Sense Operator. Additionally, there are other Kubernetes operators in Qlik Sense on Kubernetes that provide other types functionality (ex. scaling). Depending on the profile chosen [(see Deployment profiles)](#deployment-profiles), additional CRDs can also be installed for third-party components (see gke-demo).
|
||||||
|
|
||||||
|
Kubernetes operators require Custom resource definitions (CRD) (read here), which are YAML schemas for custom resources (CR). The Qlik Sense application instance, corresponding to the name of the configuration context, corresponds to a CR (ex. `qlik-default`).
|
||||||
|
|
||||||
|
CRDs require cluster scope permissions and are shared cluster-wide across namespaces. These need to be installed first (if not done previously).
|
||||||
|
|
||||||
|
To install CRDs for Qlik Sense on Kubernetes into the Kubernetes cluster.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
qliksense crds install
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Preflight Checks
|
||||||
|
|
||||||
|
To check that your environment fullfills Qlik Sense requirements
|
||||||
|
|
||||||
|
```shell
|
||||||
|
qliksense preflight all
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Qlik Sense
|
||||||
|
|
||||||
|
To install Qlik Sense into a namespace in the Kubernetes cluster where `kubectl` is pointing to
|
||||||
|
|
||||||
|
```shell
|
||||||
|
qliksense install
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
The Qlik Sense on Kubernetes CLI (`qliksense`) provides an imperative interface to many of the configurations that need to be applied against the declarative structure described in [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s).
|
The Qlik Sense on Kubernetes Operator CLI (`qliksense`) facilitates:
|
||||||
|
|
||||||
The CLI facilitates:
|
|
||||||
|
|
||||||
- Installation of QSEoK
|
- Installation of QSEoK
|
||||||
- Installation of qliksense operator to manage QSEoK
|
- Installation of Qliksense operator to manage the QSEoK installation
|
||||||
- Air gapped installation of QSEoK
|
- Air gapped installation of QSEoK
|
||||||
|
- Cluster configuration management
|
||||||
|
- Pre-flight and Post-flight environment and configuration checks
|
||||||
|
|
||||||
|
The Qlik Sense on Kubernetes Operator CLI provides an imperative interface to many of the configurations that need to be applied against the declarative structure described in the [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s) repository
|
||||||
|
|
||||||
|
To get start quickly go to the [Getting Started page](getting_started.md).
|
||||||
|
|
||||||
|
To learn more about the internal workings of the Qlik Sense on Kubernetes Operator, go to [How CLI works](concepts.md).
|
||||||
|
|
||||||
!!! info ""
|
!!! info ""
|
||||||
This is a technology preview that uses Qlik modified [kustomize](https://github.com/qlik-oss/kustomize) for Kubernetes manifests on [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s) repository
|
This is a technology preview that uses Qlik modified [kustomize](https://github.com/qlik-oss/kustomize) for Kubernetes manifests on [qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s) repository
|
||||||
|
|
||||||
|
|
||||||
!!! info ""
|
!!! info ""
|
||||||
See QlikSense [edge releases on qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s/releases) repository
|
See QlikSense [edge releases on qliksense-k8s](https://github.com/qlik-oss/qliksense-k8s/releases) repository
|
||||||
|
|||||||
51
docs/postflight_checks.md
Normal file
51
docs/postflight_checks.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Postflight checks
|
||||||
|
Postflight checks are performed after qliksense is installed on the cluster and during normal operating mode of the product. Such checks can range from validating certain conditions to checking the status of certain operations or entities on the kubernetes cluster.
|
||||||
|
|
||||||
|
Run the following command to view help about the commands supported by postflight at any moment:
|
||||||
|
```
|
||||||
|
$ qliksense postflight
|
||||||
|
perform postflight checks on the cluster
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
qliksense postflight [command]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
qliksense postflight <postflight_check_to_run>
|
||||||
|
|
||||||
|
Available Commands:
|
||||||
|
db-migration-check check mongodb migration status on the cluster
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-h, --help help for postflight
|
||||||
|
-v, --verbose verbose mode
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run all postflight checks
|
||||||
|
This command runs all the postflight checks available.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ qliksense postflight all
|
||||||
|
Running all postflight checks...
|
||||||
|
|
||||||
|
Postflight db migration check...
|
||||||
|
Logs from pod: qliksense-users-6977cb7788-qlgmv
|
||||||
|
{"caller":"main.go:39","environment":"qseok","error":"error parsing uri: scheme must be \"mongodb\" or \"mongodb+srv\"","level":"error","message":"failed to connect to ","timestamp":"2020-06-17T04:10:11.7891913Z","version":""}
|
||||||
|
To view more logs in this context, please run the command: kubectl logs -n test_ns qliksense-users-6977cb7788-qlgmv migration
|
||||||
|
PASSED
|
||||||
|
|
||||||
|
All postflight checks have PASSED
|
||||||
|
```
|
||||||
|
|
||||||
|
### DB migration check
|
||||||
|
This command checks init containers for successful database migrarion completions, and reports failure, if any to the user.
|
||||||
|
|
||||||
|
An example run of this check produces an output as shown below:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ qliksense postflight db-migration-check
|
||||||
|
Logs from pod: qliksense-users-6977cb7788-cxxwh
|
||||||
|
{"caller":"main.go:39","environment":"qseok","error":"error parsing uri: scheme must be \"mongodb\" or \"mongodb+srv\"","level":"error","message":"failed to connect to ","timestamp":"2020-06-01T01:07:18.4170507Z","version":""}
|
||||||
|
To view more logs in this context, please run the command: kubectl logs -n test_ns qliksense-users-6977cb7788-qlgmv migration
|
||||||
|
PASSED
|
||||||
|
Postflight db_migration_check completed
|
||||||
|
```
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Preflight checks provide pre-installation cluster conformance testing and validation before we install qliksense on the cluster. We gather a suite of conformance tests that can be easily written and run on the target cluster to verify that cluster-specific requirements are met.
|
Preflight checks provide pre-installation cluster conformance testing and validation before we install qliksense on the cluster. We gather a suite of conformance tests that can be easily written and run on the target cluster to verify that cluster-specific requirements are met.
|
||||||
|
|
||||||
We support the following tests at the moment as part of preflight checks, and the range of the suite will be expanded in future.
|
We support a couple of tests at the moment as part of preflight checks, and the range of the suite will be expanded in future.
|
||||||
|
|
||||||
Run the following command to view help about the commands supported by preflight at any moment:
|
Run the following command to view help about the commands supported by preflight at any moment:
|
||||||
```shell
|
```shell
|
||||||
@@ -16,19 +16,29 @@ Examples:
|
|||||||
qliksense preflight <preflight_check_to_run>
|
qliksense preflight <preflight_check_to_run>
|
||||||
|
|
||||||
Available Commands:
|
Available Commands:
|
||||||
all perform all checks
|
all perform all checks
|
||||||
dns perform preflight dns check
|
authcheck preflight authcheck
|
||||||
k8s-version check k8s version
|
clean perform preflight clean
|
||||||
|
deployment perform preflight deployment check
|
||||||
|
dns perform preflight dns check
|
||||||
|
k8s-version check kubernetes version
|
||||||
|
mongo preflight mongo OR preflight mongo --url=<url>
|
||||||
|
pod perform preflight pod check
|
||||||
|
role preflight create role check
|
||||||
|
rolebinding preflight create rolebinding check
|
||||||
|
service perform preflight service check
|
||||||
|
serviceaccount preflight create ServiceAccount check
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-h, --help help for preflight
|
-h, --help help for preflight
|
||||||
|
-v, --verbose verbose mode
|
||||||
```
|
```
|
||||||
|
|
||||||
### DNS check
|
### DNS check
|
||||||
Run the following command to perform preflight DNS check. We setup a kubernetes deployment and try to reach it as part of establishing DNS connectivity in this check.
|
Run the following command to perform preflight DNS check. We setup a kubernetes deployment and try to reach it as part of establishing DNS connectivity in this check.
|
||||||
The expected output should be similar to the one shown below.
|
The expected output should be similar to the one shown below.
|
||||||
```shell
|
```shell
|
||||||
$ qliksense preflight dns
|
$ qliksense preflight dns -v
|
||||||
|
|
||||||
Preflight DNS check
|
Preflight DNS check
|
||||||
---------------------
|
---------------------
|
||||||
@@ -51,7 +61,7 @@ Deleted deployment: dep-dns-preflight-check
|
|||||||
We check the version of the target kubernetes cluster and ensure that it falls in the valid range of kubernetes versions that are supported by qliksense.
|
We check the version of the target kubernetes cluster and ensure that it falls in the valid range of kubernetes versions that are supported by qliksense.
|
||||||
The command to run this check and the expected similar output are as shown below:
|
The command to run this check and the expected similar output are as shown below:
|
||||||
```shell
|
```shell
|
||||||
$ qliksense preflight k8s-version
|
$ qliksense preflight k8s-version -v
|
||||||
|
|
||||||
Preflight kubernetes minimum version check
|
Preflight kubernetes minimum version check
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
@@ -66,7 +76,7 @@ Completed Preflight kubernetes minimum version check
|
|||||||
### Service check
|
### Service check
|
||||||
We use the commmand below to test if we are able to create a service in the cluster.
|
We use the commmand below to test if we are able to create a service in the cluster.
|
||||||
```shell
|
```shell
|
||||||
$ qliksense preflight service
|
$ qliksense preflight service -v
|
||||||
|
|
||||||
Preflight service check
|
Preflight service check
|
||||||
-----------------------
|
-----------------------
|
||||||
@@ -82,7 +92,7 @@ Completed preflight service check
|
|||||||
### Deployment check
|
### Deployment check
|
||||||
We use the commmand below to test if we are able to create a deployment in the cluster. After the test exexutes, we wait until the created deployment terminates before we exit the command.
|
We use the commmand below to test if we are able to create a deployment in the cluster. After the test exexutes, we wait until the created deployment terminates before we exit the command.
|
||||||
```shell
|
```shell
|
||||||
$ qliksense preflight deployment
|
$ qliksense preflight deployment -v
|
||||||
|
|
||||||
Preflight deployment check
|
Preflight deployment check
|
||||||
-----------------------
|
-----------------------
|
||||||
@@ -97,7 +107,7 @@ Completed preflight deployment check
|
|||||||
### Pod check
|
### Pod check
|
||||||
We use the commmand below to test if we are able to create a pod in the cluster.
|
We use the commmand below to test if we are able to create a pod in the cluster.
|
||||||
```shell
|
```shell
|
||||||
$ qliksense preflight pod
|
$ qliksense preflight pod -v
|
||||||
|
|
||||||
Preflight pod check
|
Preflight pod check
|
||||||
--------------------
|
--------------------
|
||||||
@@ -110,61 +120,61 @@ Deleted pod: pod-pf-check
|
|||||||
Completed preflight pod check
|
Completed preflight pod check
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create-Role check
|
### Role check
|
||||||
We use the command below to test if we are able to create a role in the cluster
|
We use the command below to test if we are able to create a role in the cluster
|
||||||
```shell
|
```shell
|
||||||
$ qliksense preflight create-role
|
$ qliksense preflight role -v
|
||||||
Preflight create-role check
|
Preflight role check
|
||||||
---------------------------
|
---------------------------
|
||||||
Preflight create-role check:
|
Preflight role check:
|
||||||
Created role: role-preflight-check
|
Created role: role-preflight-check
|
||||||
Preflight create-role check: PASSED
|
Preflight role check: PASSED
|
||||||
Cleaning up resources...
|
Cleaning up resources...
|
||||||
Deleted role: role-preflight-check
|
Deleted role: role-preflight-check
|
||||||
|
|
||||||
Completed preflight create-role check
|
Completed preflight role check
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create-RoleBinding check
|
### RoleBinding check
|
||||||
We use the command below to test if we are able to create a role binding in the cluster
|
We use the command below to test if we are able to create a role binding in the cluster
|
||||||
```shell
|
```shell
|
||||||
$ qliksense preflight createRoleBinding
|
$ qliksense preflight rolebinding -v
|
||||||
|
|
||||||
Preflight create roleBinding check
|
Preflight rolebinding check
|
||||||
---------------------------
|
---------------------------
|
||||||
Preflight createRoleBinding check:
|
Preflight rolebinding check:
|
||||||
Created RoleBinding: role-binding-preflight-check
|
Created RoleBinding: role-binding-preflight-check
|
||||||
Preflight createRoleBinding check: PASSED
|
Preflight rolebinding check: PASSED
|
||||||
Cleaning up resources...
|
Cleaning up resources...
|
||||||
Deleting RoleBinding: role-binding-preflight-check
|
Deleting RoleBinding: role-binding-preflight-check
|
||||||
Deleted RoleBinding: role-binding-preflight-check
|
Deleted RoleBinding: role-binding-preflight-check
|
||||||
|
|
||||||
Completed preflight createRoleBinding check
|
Completed preflight rolebinding check
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create-ServiceAccount check
|
### Create-ServiceAccount check
|
||||||
We use the command below to test if we are able to create a service account in the cluster
|
We use the command below to test if we are able to create a service account in the cluster
|
||||||
```shell
|
```shell
|
||||||
$ qliksense preflight createServiceAccount
|
$ qliksense preflight serviceaccount -v
|
||||||
|
|
||||||
Preflight create ServiceAccount check
|
Preflight ServiceAccount check
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
Preflight createServiceAccount check:
|
Preflight serviceaccount check:
|
||||||
Created Service Account: preflight-check-test-serviceaccount
|
Created Service Account: preflight-check-test-serviceaccount
|
||||||
Preflight createServiceAccount check: PASSED
|
Preflight serviceaccount check: PASSED
|
||||||
Cleaning up resources...
|
Cleaning up resources...
|
||||||
Deleting ServiceAccount: preflight-check-test-serviceaccount
|
Deleting ServiceAccount: preflight-check-test-serviceaccount
|
||||||
Deleted ServiceAccount: preflight-check-test-serviceaccount
|
Deleted ServiceAccount: preflight-check-test-serviceaccount
|
||||||
|
|
||||||
Completed preflight createServiceAccount check
|
Completed preflight serviceaccount check
|
||||||
```
|
```
|
||||||
|
|
||||||
### CreateRB check
|
### Auth check
|
||||||
We use the command below to combine creation of role, role binding, and service account tests
|
We use the command below to combine creation of role, role binding, and service account tests
|
||||||
```shell
|
```shell
|
||||||
$ qliksense preflight createRB
|
$ qliksense preflight authcheck -v
|
||||||
|
|
||||||
Preflight createRB check
|
Preflight auth check
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
Preflight create-role check:
|
Preflight create-role check:
|
||||||
Created role: role-preflight-check
|
Created role: role-preflight-check
|
||||||
@@ -189,18 +199,18 @@ Cleaning up resources...
|
|||||||
Deleted ServiceAccount: preflight-check-test-serviceaccount
|
Deleted ServiceAccount: preflight-check-test-serviceaccount
|
||||||
|
|
||||||
Completed preflight createServiceAccount check
|
Completed preflight createServiceAccount check
|
||||||
Completed preflight CreateRB check
|
Completed preflight auth check
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mongodb check
|
### Mongodb check
|
||||||
We can check if we are able to connect to an instance of mongodb on the cluster by either supplying the mongodbUri as part of the command or infer it from the current context.
|
We can check if we are able to connect to an instance of mongodb on the cluster by either supplying the mongodbUri as part of the command or infer it from the current context.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
qliksense preflight mongo --url=<url> OR
|
qliksense preflight mongo --url=<url> -v OR
|
||||||
qliksense preflight mongo
|
qliksense preflight mongo -v
|
||||||
qliksense preflight mongo --url=<mongo-server url> --ca-cert=<path to ca-cert file>
|
qliksense preflight mongo --url=<mongo-server url> --ca-cert=<path to ca-cert file> -v
|
||||||
|
```
|
||||||
|
```shell
|
||||||
Preflight mongo check
|
Preflight mongo check
|
||||||
---------------------
|
---------------------
|
||||||
Preflight mongodb check:
|
Preflight mongodb check:
|
||||||
@@ -216,13 +226,35 @@ Deleted pod: pf-mongo-pod
|
|||||||
Completed preflight mongodb check
|
Completed preflight mongodb check
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Mongodb check with mutual tls
|
||||||
|
In order to perform mutual tls with mongo we need to:
|
||||||
|
- append client certificate to the beginning/end of CA certificate. Make sure to include the beginning and end tags on each certificate.
|
||||||
|
The CA certificate file should look like this in the end:
|
||||||
|
```shell
|
||||||
|
<existing contents of CA cert>
|
||||||
|
...
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
<private key>
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
<public key>
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
```
|
||||||
|
- Run the command below to set the ca certificate into the CR
|
||||||
|
```shell
|
||||||
|
cat <path_to_ca.crt> | base64 | qliksense config set-secrets qliksense.caCertificates --base64
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, run:
|
||||||
|
```shell
|
||||||
|
qliksense preflight mongo -v
|
||||||
|
```
|
||||||
|
|
||||||
### Running all checks
|
### Running all checks
|
||||||
Run the command shown below to execute all preflight checks.
|
Run the command shown below to execute all preflight checks.
|
||||||
```shell
|
```shell
|
||||||
$ qliksense preflight all --mongodb-url=<url> OR
|
$ qliksense preflight all --mongodb-url=<url> -v OR
|
||||||
$ qliksense preflight all --mongodb-url=<mongo-server url> --mongodb-ca-cert=<path to ca-cert file>
|
$ qliksense preflight all --mongodb-url=<mongo-server url> --mongodb-ca-cert=<path to ca-cert file> -v
|
||||||
|
|
||||||
Running all preflight checks
|
Running all preflight checks
|
||||||
|
|
||||||
@@ -254,3 +286,40 @@ All preflight checks have PASSED
|
|||||||
Completed running all preflight checks
|
Completed running all preflight checks
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Clean
|
||||||
|
Run the command below to cleanup entities that were created for the purpose of running preflight checks and left behind in the cluster.
|
||||||
|
```shell
|
||||||
|
$ qliksense preflight clean -v
|
||||||
|
|
||||||
|
Preflight clean
|
||||||
|
----------------
|
||||||
|
Removing deployment...
|
||||||
|
Removing service...
|
||||||
|
Removing pod...
|
||||||
|
Removing role...
|
||||||
|
Removing rolebinding...
|
||||||
|
Removing serviceaccount...
|
||||||
|
Removing DNS check components...
|
||||||
|
Removing mongo check components...
|
||||||
|
Preflight cleanup complete
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify-ca-chain check
|
||||||
|
We use the command below to verify the ca certificate chain and server certificate. We run this check over mongodbUrl and discoveryUrl we inferred from idpconfigs in the CR.
|
||||||
|
```shell
|
||||||
|
$ qliksense preflight preflight verify-ca-chain -v
|
||||||
|
|
||||||
|
Preflight verify-ca-chain check...
|
||||||
|
-----------------------------------
|
||||||
|
Openssl verify mongodbUrl:
|
||||||
|
Mongodb url inferred form CR: <mongodbUrl_from_CR>
|
||||||
|
Host: <host extracted from mongodbUrl>
|
||||||
|
|
||||||
|
Openssl verify discoveryUrl:
|
||||||
|
Discovery url: <discoveryUrl_from_CR>
|
||||||
|
Host: <host extracted from discoveryUrl>
|
||||||
|
Completed preflight verify-CA-chain check
|
||||||
|
PASSED
|
||||||
|
```
|
||||||
25
go.mod
25
go.mod
@@ -10,55 +10,54 @@ replace (
|
|||||||
k8s.io/client-go => k8s.io/client-go v0.17.0
|
k8s.io/client-go => k8s.io/client-go v0.17.0
|
||||||
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
|
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
|
||||||
|
|
||||||
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200424070349-b0312eb71568
|
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.5.2-0.20200820111149-1a59db58525f
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.52.0 // indirect
|
cloud.google.com/go v0.52.0 // indirect
|
||||||
cloud.google.com/go/storage v1.5.0 // indirect
|
cloud.google.com/go/storage v1.5.0 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.0.3
|
github.com/Masterminds/semver/v3 v3.1.0
|
||||||
github.com/Shopify/ejson v1.2.1
|
github.com/Shopify/ejson v1.2.1
|
||||||
github.com/aws/aws-sdk-go v1.28.9 // indirect
|
github.com/aws/aws-sdk-go v1.28.9 // indirect
|
||||||
github.com/bugsnag/bugsnag-go v1.5.3 // indirect
|
github.com/bugsnag/bugsnag-go v1.5.3 // indirect
|
||||||
github.com/containers/image/v5 v5.1.0
|
github.com/containers/image/v5 v5.1.0
|
||||||
github.com/docker/go-metrics v0.0.1 // indirect
|
github.com/docker/go-metrics v0.0.1 // indirect
|
||||||
github.com/go-git/go-git/v5 v5.0.0
|
github.com/go-git/go-git/v5 v5.1.0
|
||||||
github.com/gobuffalo/envy v1.9.0 // indirect
|
github.com/gobuffalo/envy v1.9.0 // indirect
|
||||||
github.com/gobuffalo/logger v1.0.3 // indirect
|
github.com/gobuffalo/logger v1.0.3 // indirect
|
||||||
github.com/gobuffalo/packd v1.0.0 // indirect
|
github.com/gobuffalo/packd v1.0.0 // indirect
|
||||||
github.com/gobuffalo/packr/v2 v2.7.1
|
github.com/gobuffalo/packr/v2 v2.7.1
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible // indirect
|
github.com/gofrs/uuid v3.2.0+incompatible // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||||
github.com/golang/protobuf v1.3.3 // indirect
|
|
||||||
github.com/gorilla/mux v1.7.3 // indirect
|
github.com/gorilla/mux v1.7.3 // indirect
|
||||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||||
|
|
||||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381
|
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381
|
||||||
|
|
||||||
github.com/mattn/go-colorable v0.1.4
|
github.com/mattn/go-colorable v0.1.4
|
||||||
github.com/mattn/go-tty v0.0.3
|
github.com/mattn/go-tty v0.0.3
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/otiai10/copy v1.1.1
|
github.com/otiai10/copy v1.1.1
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/qlik-oss/k-apis v0.1.1
|
github.com/qlik-oss/k-apis v0.1.16
|
||||||
|
github.com/rancher/k3d/v3 v3.0.2 // indirect
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
||||||
github.com/spf13/cobra v0.0.6
|
github.com/sirupsen/logrus v1.7.0 // indirect
|
||||||
|
github.com/spf13/cobra v1.0.1-0.20200629195214-2c5a0d300f8b
|
||||||
github.com/spf13/viper v1.6.1
|
github.com/spf13/viper v1.6.1
|
||||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 // indirect
|
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 // indirect
|
||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a // indirect
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a // indirect
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
golang.org/x/net v0.0.0-20200602114024-627f9648deb9
|
||||||
golang.org/x/tools v0.0.0-20200312194400-c312e98713c2 // indirect
|
golang.org/x/tools v0.0.0-20200312194400-c312e98713c2 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b // indirect
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
gopkg.in/yaml.v2 v2.2.8
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20190924164351-c8b7dadae555
|
|
||||||
k8s.io/api v0.17.2
|
k8s.io/api v0.17.2
|
||||||
|
k8s.io/apiextensions-apiserver v0.17.2
|
||||||
k8s.io/apimachinery v0.17.2
|
k8s.io/apimachinery v0.17.2
|
||||||
k8s.io/client-go v11.0.0+incompatible
|
k8s.io/client-go v11.0.0+incompatible
|
||||||
|
k8s.io/kubectl v0.17.2
|
||||||
sigs.k8s.io/kustomize/api v0.3.2
|
sigs.k8s.io/kustomize/api v0.3.2
|
||||||
sigs.k8s.io/yaml v1.1.0
|
sigs.k8s.io/yaml v1.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
exclude github.com/Azure/go-autorest v12.0.0+incompatible
|
exclude github.com/Azure/go-autorest v12.0.0+incompatible
|
||||||
|
|||||||
166
go.sum
166
go.sum
@@ -32,6 +32,7 @@ contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcig
|
|||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg=
|
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg=
|
||||||
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0=
|
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0=
|
||||||
|
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
|
||||||
github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU=
|
github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU=
|
||||||
github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo=
|
github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo=
|
||||||
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
||||||
@@ -72,6 +73,8 @@ github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RP
|
|||||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14=
|
github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14=
|
||||||
github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8=
|
github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8=
|
||||||
github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU=
|
github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU=
|
||||||
github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
|
github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
|
||||||
@@ -79,9 +82,12 @@ github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6tr
|
|||||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||||
github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZpg=
|
github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZpg=
|
||||||
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
|
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
|
||||||
|
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
|
||||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/OpenPeeDeeP/depguard v1.0.0/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o=
|
||||||
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
|
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
|
||||||
|
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||||
@@ -104,6 +110,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
|||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||||
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6 h1:bZ28Hqta7TFAK3Q08CMvv8y3/8ATaEqv2nGoc6yff6c=
|
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6 h1:bZ28Hqta7TFAK3Q08CMvv8y3/8ATaEqv2nGoc6yff6c=
|
||||||
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U=
|
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U=
|
||||||
|
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
@@ -121,6 +128,7 @@ github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06
|
|||||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||||
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||||
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||||
|
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||||
github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
@@ -159,29 +167,39 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
|||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||||
|
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f h1:tSNMc+rJDfmYntojat8lljbt1mgKNpTxUZJsSzJ9Y1s=
|
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f h1:tSNMc+rJDfmYntojat8lljbt1mgKNpTxUZJsSzJ9Y1s=
|
||||||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
|
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
|
||||||
|
github.com/containerd/cgroups v0.0.0-20190923161937-abd0b19954a6/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
|
||||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||||
github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||||
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||||
github.com/containerd/containerd v1.3.2 h1:ForxmXkA6tPIvffbrDAcPUIB32QgXkt2XFj+F0UxetA=
|
github.com/containerd/containerd v1.3.2 h1:ForxmXkA6tPIvffbrDAcPUIB32QgXkt2XFj+F0UxetA=
|
||||||
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||||
|
github.com/containerd/containerd v1.3.4 h1:3o0smo5SKY7H6AJCmJhsnCjR2/V2T8VmiHt7seN2/kI=
|
||||||
|
github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||||
github.com/containerd/continuity v0.0.0-20180216233310-d8fb8589b0e8/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
github.com/containerd/continuity v0.0.0-20180216233310-d8fb8589b0e8/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||||
|
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||||
github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41 h1:kIFnQBO7rQ0XkMe6xEwbybYHBEaWmh/f++laI6Emt7M=
|
github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41 h1:kIFnQBO7rQ0XkMe6xEwbybYHBEaWmh/f++laI6Emt7M=
|
||||||
github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
|
github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
|
||||||
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||||
|
github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
||||||
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||||
|
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8 h1:jYCTS/16RWXXtVHNHo1KWNegd1kKQ7lHd7BStj/0hKw=
|
||||||
|
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd h1:JNn81o/xG+8NEo3bC/vx9pbi/g2WI8mtP2/nXzu297Y=
|
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd h1:JNn81o/xG+8NEo3bC/vx9pbi/g2WI8mtP2/nXzu297Y=
|
||||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||||
|
github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
|
||||||
github.com/containers/image/v5 v5.1.0 h1:5FjAvPJniamuNNIQHkh4PnsL+n+xzs6Aonzaz5dqTEo=
|
github.com/containers/image/v5 v5.1.0 h1:5FjAvPJniamuNNIQHkh4PnsL+n+xzs6Aonzaz5dqTEo=
|
||||||
github.com/containers/image/v5 v5.1.0/go.mod h1:BKlMD34WxRo1ruGHHEOrPQP0Qci7SWoPwU6fS7arsCU=
|
github.com/containers/image/v5 v5.1.0/go.mod h1:BKlMD34WxRo1ruGHHEOrPQP0Qci7SWoPwU6fS7arsCU=
|
||||||
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
|
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
|
||||||
@@ -227,7 +245,9 @@ github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyG
|
|||||||
github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492 h1:FwssHbCDJD025h+BchanCwE1Q8fyMgqDr2mOQAWOLGw=
|
github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492 h1:FwssHbCDJD025h+BchanCwE1Q8fyMgqDr2mOQAWOLGw=
|
||||||
github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
|
github.com/docker/distribution v0.0.0-20171011171712-7484e51bf6af/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
||||||
|
github.com/docker/distribution v0.0.0-20200319173657-742aab907b54/go.mod h1:Oqz4IonmMNc2N7GqfTL2xkhCQx0yS6nR+HrOZJnmKIk=
|
||||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
|
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
|
||||||
@@ -235,6 +255,7 @@ github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avu
|
|||||||
github.com/docker/go-connections v0.0.0-20180212134524-7beb39f0b969/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.0.0-20180212134524-7beb39f0b969/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
|
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
|
||||||
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||||
@@ -256,6 +277,7 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb
|
|||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA=
|
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA=
|
||||||
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ=
|
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ=
|
||||||
|
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
|
||||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
|
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
|
||||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
@@ -263,7 +285,9 @@ github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao
|
|||||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM=
|
github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM=
|
||||||
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
|
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
|
||||||
@@ -273,6 +297,7 @@ github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
|
|||||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
|
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
|
||||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||||
|
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
@@ -296,8 +321,12 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
|
|||||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
|
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
|
||||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
|
github.com/go-critic/go-critic v0.3.5-0.20190526074819-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
||||||
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
||||||
|
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||||
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||||
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
|
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
|
||||||
@@ -306,6 +335,8 @@ github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp
|
|||||||
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||||
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
|
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
|
||||||
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
|
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
|
||||||
|
github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk=
|
||||||
|
github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
@@ -351,10 +382,13 @@ github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcs
|
|||||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||||
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
|
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
|
||||||
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||||
|
github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
|
||||||
|
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
||||||
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
|
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
|
||||||
|
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
|
||||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||||
@@ -364,10 +398,13 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
|
|||||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||||
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||||
|
github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
|
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
|
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
|
||||||
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
|
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
|
||||||
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
|
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
|
||||||
@@ -404,12 +441,14 @@ github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc=
|
|||||||
github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gogo/googleapis v1.3.0/go.mod h1:d+q1s/xVJxZGKWwC/6UfPIF33J+G1Tq4GYv9Y+Tg/EU=
|
||||||
github.com/gogo/protobuf v0.0.0-20170815085658-fcdc5011193f/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v0.0.0-20170815085658-fcdc5011193f/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
|
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
|
||||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
|
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721 h1:KRMr9A3qfbVM7iV/WcLY/rL5LICqwMHLhwRXKu99fXw=
|
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721 h1:KRMr9A3qfbVM7iV/WcLY/rL5LICqwMHLhwRXKu99fXw=
|
||||||
@@ -423,6 +462,7 @@ github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF
|
|||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
@@ -436,17 +476,30 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs
|
|||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
|
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
|
||||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
|
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
|
||||||
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
|
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
|
||||||
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
|
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
|
||||||
|
github.com/golangci/go-tools v0.0.0-20190318055746-e32c54105b7c/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM=
|
||||||
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
|
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
|
||||||
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
|
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
|
||||||
|
github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
|
||||||
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
|
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
|
||||||
|
github.com/golangci/golangci-lint v1.17.2-0.20190909185456-6163a8a79084/go.mod h1:jXakAOSd+FMU9dP3D6IfBK7HyD1q/RLHI9NOY8veycY=
|
||||||
github.com/golangci/golangci-lint v1.21.0/go.mod h1:phxpHK52q7SE+5KpPnti4oZTdFCEsn/tKN+nFvCKXfk=
|
github.com/golangci/golangci-lint v1.21.0/go.mod h1:phxpHK52q7SE+5KpPnti4oZTdFCEsn/tKN+nFvCKXfk=
|
||||||
|
github.com/golangci/gosec v0.0.0-20190211064107-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU=
|
||||||
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
|
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
|
||||||
|
github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
|
||||||
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
|
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
|
||||||
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
|
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
|
||||||
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
|
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
|
||||||
@@ -467,10 +520,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
|
||||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
|
||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
|
||||||
github.com/google/go-replayers/grpcreplay v0.1.0 h1:eNb1y9rZFmY4ax45uEEECSa8fsxGRU+8Bil52ASAwic=
|
github.com/google/go-replayers/grpcreplay v0.1.0 h1:eNb1y9rZFmY4ax45uEEECSa8fsxGRU+8Bil52ASAwic=
|
||||||
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
|
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
|
||||||
github.com/google/go-replayers/httpreplay v0.1.0 h1:AX7FUb4BjrrzNvblr/OlgwrmFiep6soj5K2QSDW7BGk=
|
github.com/google/go-replayers/httpreplay v0.1.0 h1:AX7FUb4BjrrzNvblr/OlgwrmFiep6soj5K2QSDW7BGk=
|
||||||
@@ -549,6 +598,8 @@ github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVo
|
|||||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-getter v1.4.1 h1:3A2Mh8smGFcf5M+gmcv898mZdrxpseik45IpcyISLsA=
|
||||||
|
github.com/hashicorp/go-getter v1.4.1/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=
|
||||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||||
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||||
@@ -596,6 +647,7 @@ github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8
|
|||||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
|
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
@@ -611,6 +663,8 @@ github.com/hashicorp/vault/sdk v0.1.13 h1:mOEPeOhT7jl0J4AMl1E705+BcmeRs1VmKNb9F0
|
|||||||
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
|
github.com/heroku/docker-registry-client v0.0.0-20190909225348-afc9e1acc3d5 h1:6ZR6HQ+P9ZUwHlYq+bU7e9wqAImxKUguq8fp2gZSgCo=
|
||||||
|
github.com/heroku/docker-registry-client v0.0.0-20190909225348-afc9e1acc3d5/go.mod h1:Yho0S7KhsnHQRCC5lDraYF1SsLMeWtf/tKdufKu3TJA=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
|
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
|
||||||
@@ -620,6 +674,8 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
|
|||||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
|
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
||||||
|
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
@@ -654,6 +710,7 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v
|
|||||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
|
github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
@@ -669,6 +726,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGi
|
|||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
@@ -691,6 +749,7 @@ github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e h1:9MlwzLdW7QSD
|
|||||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
|
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
|
||||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
|
github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
@@ -717,6 +776,7 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
|||||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k=
|
github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k=
|
||||||
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-shellwords v1.0.6 h1:9Jok5pILi5S1MnDirGVTufYGtksUs/V2BWUP3ZkeUUI=
|
github.com/mattn/go-shellwords v1.0.6 h1:9Jok5pILi5S1MnDirGVTufYGtksUs/V2BWUP3ZkeUUI=
|
||||||
@@ -743,6 +803,7 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
|
|||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||||
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||||
@@ -753,6 +814,7 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb
|
|||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||||
@@ -760,6 +822,9 @@ github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/I
|
|||||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 h1:cvy4lBOYN3gKfKj8Lzz5Q9TfviP+L7koMHY7SvkyTKs=
|
github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 h1:cvy4lBOYN3gKfKj8Lzz5Q9TfviP+L7koMHY7SvkyTKs=
|
||||||
github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
|
github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
|
||||||
|
github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
|
||||||
|
github.com/moby/sys/mountinfo v0.1.0/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
|
||||||
|
github.com/moby/term v0.0.0-20200507201656-73f35e472e8f/go.mod h1:uF4OSdW39LLr+K/v/iL6dOm257SGdQJGiyMU1QlNd6s=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -767,8 +832,11 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
|
|||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
|
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
|
github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||||
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||||
github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c h1:xa+eQWKuJ9MbB9FBL/eoNvDFvveAkz2LQoz8PzX7Q/4=
|
github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c h1:xa+eQWKuJ9MbB9FBL/eoNvDFvveAkz2LQoz8PzX7Q/4=
|
||||||
github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c/go.mod h1:GhAqVMEWnTcW2dxoD/SO3n2enrgWl3y6Dnx4m59GvcA=
|
github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c/go.mod h1:GhAqVMEWnTcW2dxoD/SO3n2enrgWl3y6Dnx4m59GvcA=
|
||||||
@@ -776,6 +844,8 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
|
github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||||
|
github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||||
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||||
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
@@ -791,12 +861,15 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
|||||||
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
|
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
|
||||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
|
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
@@ -808,6 +881,7 @@ github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59P
|
|||||||
github.com/opencontainers/runc v1.0.0-rc9 h1:/k06BMULKF5hidyoZymkoDCzdJzltZpz/UU4LguQVtc=
|
github.com/opencontainers/runc v1.0.0-rc9 h1:/k06BMULKF5hidyoZymkoDCzdJzltZpz/UU4LguQVtc=
|
||||||
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||||
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
|
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||||
github.com/opencontainers/selinux v1.3.0 h1:xsI95WzPZu5exzA6JzkLSfdr/DilzOhCJOqGe5TgR0g=
|
github.com/opencontainers/selinux v1.3.0 h1:xsI95WzPZu5exzA6JzkLSfdr/DilzOhCJOqGe5TgR0g=
|
||||||
github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
|
github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
|
||||||
@@ -824,8 +898,10 @@ github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH
|
|||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
|
||||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||||
|
github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||||
@@ -881,13 +957,20 @@ 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 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
|
||||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
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/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/qlik-oss/k-apis v0.1.1 h1:aZ4eTMB3mSn03Kuj7+RI0eFLkjK9+0qxADBioRb3qVA=
|
github.com/qlik-oss/k-apis v0.1.16 h1:R3gCZs4A3EHPNx4B7p1idWD+OhyaU/bAlGYBWc0ZNz4=
|
||||||
github.com/qlik-oss/k-apis v0.1.1/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
|
github.com/qlik-oss/k-apis v0.1.16/go.mod h1:AkNa/kaZHpGVs9l+pHe6nvz99Sp9WO1f9ylBES95o+I=
|
||||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200424070349-b0312eb71568 h1:wHOUCGfnmgYqW3aCjuP3fXmB2T/uZXMvltO+F3us83E=
|
github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b h1:RDh3OZJOriy/ap1NUHVKsPG07N4DALaCzaqXFFK57T0=
|
||||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200424070349-b0312eb71568/go.mod h1:Yg8bqX8Mq/eSgXfcenxCxhZuSXg+NCsKq6NBdch/oUc=
|
github.com/qlik-oss/kustomize/api v0.3.3-0.20200612023448-4c1f2f38ea9b/go.mod h1:zh3yFgE5zFk1kreqzVyyj1eXyIxQJT53l4zSg8Wt4SA=
|
||||||
|
github.com/qlik-oss/kustomize/api v0.5.2-0.20200820111149-1a59db58525f h1:a6TNAGEyKqlMzDQwwoVwBjcRZ47ZnXxk3ue+zy5I4Qk=
|
||||||
|
github.com/qlik-oss/kustomize/api v0.5.2-0.20200820111149-1a59db58525f/go.mod h1:H7m776WsaBG9jFljChQXd2bGlzTJvQRHDESRQ96psXU=
|
||||||
|
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA=
|
||||||
|
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
|
||||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
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 h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
|
||||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
|
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
|
||||||
|
github.com/rancher/k3d v1.7.0 h1:GEN/lmtMrklqfBh4Jha06OE6cLOeKwgTFmG/UgOR6VA=
|
||||||
|
github.com/rancher/k3d/v3 v3.0.2 h1:/bz56A/mojFD/wbkCFE6knzRMjMtCCtMURD54OPkEY0=
|
||||||
|
github.com/rancher/k3d/v3 v3.0.2/go.mod h1:+55GxRRh7dJarpVV6IbIo96zwxylhQ4gJjGrkmBQ3cs=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
@@ -905,6 +988,7 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR
|
|||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
||||||
@@ -919,6 +1003,7 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
|||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0=
|
github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0=
|
||||||
github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM=
|
github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM=
|
||||||
|
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||||
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
|
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
|
||||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
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 v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||||
@@ -926,12 +1011,16 @@ github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOms
|
|||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
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/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.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||||
|
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||||
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
|
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||||
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
@@ -941,20 +1030,27 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
|
|||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
|
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
|
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
|
github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||||
github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
|
github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
|
||||||
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
|
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
|
github.com/spf13/cobra v1.0.1-0.20200629195214-2c5a0d300f8b h1:grM+VdcoRu+xbzmCXM1KuH5UQGk9Lc8yCiwZZ2PKVdU=
|
||||||
|
github.com/spf13/cobra v1.0.1-0.20200629195214-2c5a0d300f8b/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
|
github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
@@ -964,6 +1060,7 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
|||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
|
github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
|
||||||
@@ -975,6 +1072,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
@@ -986,6 +1084,7 @@ github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG
|
|||||||
github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs=
|
github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs=
|
||||||
github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
|
github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
|
||||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
|
github.com/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||||
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
@@ -999,6 +1098,7 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY
|
|||||||
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||||
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
|
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
|
||||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||||
|
github.com/ultraware/funlen v0.0.1/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||||
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||||
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
|
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
|
||||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
@@ -1007,6 +1107,7 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
|
|||||||
github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
|
github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
|
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
|
||||||
|
github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
|
||||||
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
|
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
|
||||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||||
github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE=
|
github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE=
|
||||||
@@ -1029,6 +1130,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofm
|
|||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
||||||
|
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
||||||
|
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
github.com/yujunz/go-getter v1.4.1-lite h1:FhvNc94AXMZkfqUwfMKhnQEC9phkphSGdPTL7tIdhOM=
|
github.com/yujunz/go-getter v1.4.1-lite h1:FhvNc94AXMZkfqUwfMKhnQEC9phkphSGdPTL7tIdhOM=
|
||||||
github.com/yujunz/go-getter v1.4.1-lite/go.mod h1:sbmqxXjyLunH1PkF3n7zSlnVeMvmYUuIl9ZVs/7NyCc=
|
github.com/yujunz/go-getter v1.4.1-lite/go.mod h1:sbmqxXjyLunH1PkF3n7zSlnVeMvmYUuIl9ZVs/7NyCc=
|
||||||
@@ -1047,6 +1150,7 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
|||||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||||
|
go.mongodb.org/mongo-driver v1.1.2 h1:jxcFYjlkl8xaERsgLo+RNquI0epW6zuy/ZRQs6jnrFA=
|
||||||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||||
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
|
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
@@ -1054,6 +1158,9 @@ go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
|||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
|
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||||
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
|
||||||
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
@@ -1068,6 +1175,7 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
@@ -1125,6 +1233,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -1161,12 +1271,14 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8ou
|
|||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
|
||||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200528225125-3c3fba18258b h1:IYiJPiJfzktmDAO1HQiwjMjwjlYKHAL7KzeD544RJPs=
|
||||||
|
golang.org/x/net v0.0.0-20200528225125-3c3fba18258b/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
|
||||||
|
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@@ -1190,6 +1302,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -1202,6 +1315,9 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZe
|
|||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||||
|
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -1210,6 +1326,7 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
@@ -1310,6 +1427,9 @@ google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvx
|
|||||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b h1:c8OBoXP3kTbDWWB/oVE3FkR851p4iZ3MPadz7zXEIPU=
|
google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b h1:c8OBoXP3kTbDWWB/oVE3FkR851p4iZ3MPadz7zXEIPU=
|
||||||
google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20200604104852-0b0486081ffb h1:ek2py5bOqzR7MR/6obzk0rXUgYCLmjyLnaO9ssT+l6w=
|
||||||
|
google.golang.org/genproto v0.0.0-20200604104852-0b0486081ffb/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
@@ -1322,9 +1442,22 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
|
|||||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
|
google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
|
||||||
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
|
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
|
||||||
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
|
||||||
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
||||||
@@ -1337,6 +1470,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
|
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
@@ -1369,12 +1503,17 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20190924164351-c8b7dadae555 h1:4Yrwvx9yMvZx+vK3wdX7aX2UCNZJJn0TDc+BNOJTE00=
|
gopkg.in/yaml.v3 v3.0.0-20190924164351-c8b7dadae555 h1:4Yrwvx9yMvZx+vK3wdX7aX2UCNZJJn0TDc+BNOJTE00=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20190924164351-c8b7dadae555/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20190924164351-c8b7dadae555/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
gotest.tools/v3 v3.0.0 h1:d+tVGRu6X0ZBQ+kyAR8JKi6AXhTP2gmQaoIYaGFz634=
|
gotest.tools/v3 v3.0.0 h1:d+tVGRu6X0ZBQ+kyAR8JKi6AXhTP2gmQaoIYaGFz634=
|
||||||
gotest.tools/v3 v3.0.0/go.mod h1:TUP+/YtXl/dp++T+SZ5v2zUmLVBHmptSb/ajDLCJ+3c=
|
gotest.tools/v3 v3.0.0/go.mod h1:TUP+/YtXl/dp++T+SZ5v2zUmLVBHmptSb/ajDLCJ+3c=
|
||||||
|
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||||
helm.sh/helm/v3 v3.1.2 h1:VpNzaNv2DX4aRnOCcV7v5Of+XT2SZrJ8iOQ25AGKOos=
|
helm.sh/helm/v3 v3.1.2 h1:VpNzaNv2DX4aRnOCcV7v5Of+XT2SZrJ8iOQ25AGKOos=
|
||||||
helm.sh/helm/v3 v3.1.2/go.mod h1:WYsFJuMASa/4XUqLyv54s0U/f3mlAaRErGmyy4z921g=
|
helm.sh/helm/v3 v3.1.2/go.mod h1:WYsFJuMASa/4XUqLyv54s0U/f3mlAaRErGmyy4z921g=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
@@ -1418,6 +1557,8 @@ k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
|||||||
k8s.io/metrics v0.0.0-20191214191643-6b1944c9f765/go.mod h1:5V7rewilItwK0cz4nomU0b3XCcees2Ka5EBYWS1HBeM=
|
k8s.io/metrics v0.0.0-20191214191643-6b1944c9f765/go.mod h1:5V7rewilItwK0cz4nomU0b3XCcees2Ka5EBYWS1HBeM=
|
||||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
|
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
|
||||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||||
|
k8s.io/utils v0.0.0-20200109141947-94aeca20bf09 h1:sz6xjn8QP74104YNmJpzLbJ+a3ZtHt0tkD0g8vpdWNw=
|
||||||
|
k8s.io/utils v0.0.0-20200109141947-94aeca20bf09/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
|
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
|
||||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||||
@@ -1425,6 +1566,7 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs
|
|||||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||||
|
mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY=
|
||||||
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
||||||
pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
|
pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
@@ -1432,9 +1574,13 @@ rsc.io/letsencrypt v0.0.3 h1:H7xDfhkaFFSYEJlKeq38RwX2jYcnTeHuDQyT+mMNMwM=
|
|||||||
rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
|
rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
|
||||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||||
|
sigs.k8s.io/kustomize/kyaml v0.4.1 h1:NEqA/35upoAjb+I5vh1ODUqxoX4DOrezeQa9BhhG5Co=
|
||||||
|
sigs.k8s.io/kustomize/kyaml v0.4.1/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
|
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
|
||||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||||
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
site_name: Qlik Sense on Kubernetes CLI
|
site_name: Qlik Sense on Kubernetes CLI
|
||||||
repo_url: 'https://github.com/qlik-oss/sense-installer'
|
repo_url: 'https://github.com/qlik-oss/sense-installer'
|
||||||
strict: true
|
strict: true
|
||||||
|
|
||||||
theme:
|
theme:
|
||||||
name: "material"
|
name: "material"
|
||||||
palette:
|
palette:
|
||||||
primary: 'green'
|
primary: 'green'
|
||||||
accent: 'indigo'
|
accent: 'indigo'
|
||||||
|
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- toc:
|
- toc:
|
||||||
permalink: true
|
permalink: true
|
||||||
@@ -14,6 +16,8 @@ markdown_extensions:
|
|||||||
- pymdownx.inlinehilite
|
- pymdownx.inlinehilite
|
||||||
- pymdownx.superfences
|
- pymdownx.superfences
|
||||||
- pymdownx.details
|
- pymdownx.details
|
||||||
|
- pymdownx.tabbed
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
- Overview: index.md
|
- Overview: index.md
|
||||||
- getting_started.md
|
- getting_started.md
|
||||||
|
|||||||
@@ -144,19 +144,21 @@ func (cr *QliksenseCR) IsRepoExist() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cr *QliksenseCR) GetFetchUrl() string {
|
func (cr *QliksenseCR) GetFetchUrl() string {
|
||||||
if cr.Spec.FetchSource == nil || cr.Spec.FetchSource.Repository == "" {
|
if cr.Spec.Git == nil || cr.Spec.Git.Repository == "" {
|
||||||
return QLIK_GIT_REPO
|
return QLIK_GIT_REPO
|
||||||
}
|
}
|
||||||
return cr.Spec.FetchSource.Repository
|
return cr.Spec.Git.Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cr *QliksenseCR) GetFetchAccessToken(encryptionKey string) string {
|
func (cr *QliksenseCR) GetFetchAccessToken(encryptionKey string) string {
|
||||||
if cr.Spec.FetchSource == nil {
|
if cr.Spec.Git == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
if tok, err := cr.Spec.FetchSource.GetAccessToken(); err != nil {
|
if tok, err := cr.Spec.Git.GetAccessToken(); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return ""
|
return ""
|
||||||
|
} else if tok == "" {
|
||||||
|
return tok
|
||||||
} else {
|
} else {
|
||||||
by, _ := b64.StdEncoding.DecodeString(tok)
|
by, _ := b64.StdEncoding.DecodeString(tok)
|
||||||
res, err := DecryptData(by, encryptionKey)
|
res, err := DecryptData(by, encryptionKey)
|
||||||
@@ -169,29 +171,29 @@ func (cr *QliksenseCR) GetFetchAccessToken(encryptionKey string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cr *QliksenseCR) SetFetchUrl(url string) {
|
func (cr *QliksenseCR) SetFetchUrl(url string) {
|
||||||
if cr.Spec.FetchSource == nil {
|
if cr.Spec.Git == nil {
|
||||||
cr.Spec.FetchSource = &config.Repo{}
|
cr.Spec.Git = &config.Repo{}
|
||||||
}
|
}
|
||||||
cr.Spec.FetchSource.Repository = url
|
cr.Spec.Git.Repository = url
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cr *QliksenseCR) SetFetchAccessToken(token, encryptionKey string) error {
|
func (cr *QliksenseCR) SetFetchAccessToken(token, encryptionKey string) error {
|
||||||
if cr.Spec.FetchSource == nil {
|
if cr.Spec.Git == nil {
|
||||||
cr.Spec.FetchSource = &config.Repo{}
|
cr.Spec.Git = &config.Repo{}
|
||||||
}
|
}
|
||||||
res, err := EncryptData([]byte(token), encryptionKey)
|
res, err := EncryptData([]byte(token), encryptionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cr.Spec.FetchSource.AccessToken = b64.StdEncoding.EncodeToString(res)
|
cr.Spec.Git.AccessToken = b64.StdEncoding.EncodeToString(res)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cr *QliksenseCR) SetFetchAccessSecretName(sec string) {
|
func (cr *QliksenseCR) SetFetchAccessSecretName(sec string) {
|
||||||
if cr.Spec.FetchSource == nil {
|
if cr.Spec.Git == nil {
|
||||||
cr.Spec.FetchSource = &config.Repo{}
|
cr.Spec.Git = &config.Repo{}
|
||||||
}
|
}
|
||||||
cr.Spec.FetchSource.SecretName = sec
|
cr.Spec.Git.SecretName = sec
|
||||||
}
|
}
|
||||||
|
|
||||||
//DeleteRepo delete the manifest repo and unset manifestsRoot
|
//DeleteRepo delete the manifest repo and unset manifestsRoot
|
||||||
@@ -522,9 +524,9 @@ func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error)
|
|||||||
}
|
}
|
||||||
newCr.Spec.Secrets = finalSecrets
|
newCr.Spec.Secrets = finalSecrets
|
||||||
|
|
||||||
if newCr.Spec.FetchSource != nil && newCr.Spec.FetchSource.AccessToken != "" {
|
if newCr.Spec.Git != nil && newCr.Spec.Git.AccessToken != "" {
|
||||||
decData := cr.GetFetchAccessToken(encryptionKey)
|
decData := cr.GetFetchAccessToken(encryptionKey)
|
||||||
newCr.Spec.FetchSource.AccessToken = decData
|
newCr.Spec.Git.AccessToken = decData
|
||||||
}
|
}
|
||||||
return newCr, nil
|
return newCr, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ func TestGetDecryptedCr(t *testing.T) {
|
|||||||
key, _ := setupGenerateKey(dir)
|
key, _ := setupGenerateKey(dir)
|
||||||
ecn, _ := EncryptData([]byte("mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"), key)
|
ecn, _ := EncryptData([]byte("mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"), key)
|
||||||
b := b64.StdEncoding.EncodeToString(ecn)
|
b := b64.StdEncoding.EncodeToString(ecn)
|
||||||
qcr.Spec.AddToSecrets("qliksense", "mongoDbUri", b, "")
|
qcr.Spec.AddToSecrets("qliksense", "mongodbUri", b, "")
|
||||||
|
|
||||||
qcr.SetFetchAccessToken("mytoken", key)
|
qcr.SetFetchAccessToken("mytoken", key)
|
||||||
|
|
||||||
@@ -117,8 +117,8 @@ func TestGetDecryptedCr(t *testing.T) {
|
|||||||
t.Log(err)
|
t.Log(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptedValue := newCr.Spec.GetFromSecrets("qliksense", "mongoDbUri")
|
decryptedValue := newCr.Spec.GetFromSecrets("qliksense", "mongodbUri")
|
||||||
orignalValue := qcr.Spec.GetFromSecrets("qliksense", "mongoDbUri")
|
orignalValue := qcr.Spec.GetFromSecrets("qliksense", "mongodbUri")
|
||||||
if decryptedValue != "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false" {
|
if decryptedValue != "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false" {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
b, _ := K8sToYaml(newCr)
|
b, _ := K8sToYaml(newCr)
|
||||||
@@ -127,7 +127,7 @@ func TestGetDecryptedCr(t *testing.T) {
|
|||||||
if decryptedValue == orignalValue {
|
if decryptedValue == orignalValue {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
if newCr.Spec.FetchSource.AccessToken != "mytoken" {
|
if newCr.Spec.Git.AccessToken != "mytoken" {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
td()
|
td()
|
||||||
|
|||||||
727
pkg/api/clientgo_utils.go
Normal file
727
pkg/api/clientgo_utils.go
Normal file
@@ -0,0 +1,727 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
"k8s.io/client-go/util/retry"
|
||||||
|
)
|
||||||
|
|
||||||
|
var gracePeriod int64 = 0
|
||||||
|
|
||||||
|
var waitTimeout = 2 * time.Minute
|
||||||
|
|
||||||
|
type ClientGoUtils struct {
|
||||||
|
Verbose bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) LogVerboseMessage(strMessage string, args ...interface{}) {
|
||||||
|
if p.Verbose || os.Getenv("QLIKSENSE_DEBUG") == "true" {
|
||||||
|
fmt.Printf(strMessage, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func int32Ptr(i int32) *int32 { return &i }
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) LoadKubeConfigAndNamespace() (string, []byte, error) {
|
||||||
|
LogDebugMessage("Reading .kube/config file...")
|
||||||
|
|
||||||
|
homeDir, err := homedir.Dir()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to deduce home dir")
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
LogDebugMessage("Kube config location: %s\n\n", filepath.Join(homeDir, ".kube", "config"))
|
||||||
|
|
||||||
|
kubeConfig := filepath.Join(homeDir, ".kube", "config")
|
||||||
|
kubeConfigContents, err := ioutil.ReadFile(kubeConfig)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to deduce home dir")
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieve namespace
|
||||||
|
namespace := GetKubectlNamespace()
|
||||||
|
// if namespace comes back empty, we will run checks in the default namespace
|
||||||
|
if namespace == "" {
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
return namespace, kubeConfigContents, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) RetryOnError(mf func() error) error {
|
||||||
|
return retry.OnError(wait.Backoff{
|
||||||
|
Duration: 1 * time.Second,
|
||||||
|
Factor: 1,
|
||||||
|
Jitter: 0.1,
|
||||||
|
Steps: 5,
|
||||||
|
}, func(err error) bool {
|
||||||
|
return k8serrors.IsConflict(err) || k8serrors.IsGone(err) || k8serrors.IsServerTimeout(err) ||
|
||||||
|
k8serrors.IsServiceUnavailable(err) || k8serrors.IsTimeout(err) || k8serrors.IsTooManyRequests(err)
|
||||||
|
}, mf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) GetK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clientset, *rest.Config, error) {
|
||||||
|
var clientConfig *rest.Config
|
||||||
|
var err error
|
||||||
|
if len(kubeconfig) == 0 {
|
||||||
|
clientConfig, err = rest.InClusterConfig()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to load in-cluster kubeconfig: %w", err)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config, err := clientcmd.Load(kubeconfig)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to load kubeconfig: %w", err)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if contextName != "" {
|
||||||
|
config.CurrentContext = contextName
|
||||||
|
}
|
||||||
|
clientConfig, err = clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to create client config from config: %w", err)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clientset, err := kubernetes.NewForConfig(clientConfig)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to create clientset: %w", err)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return clientset, clientConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) CreatePreflightTestDeployment(clientset kubernetes.Interface, namespace string, depName string, imageName string) (*appsv1.Deployment, error) {
|
||||||
|
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
||||||
|
deployment := &appsv1.Deployment{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: depName,
|
||||||
|
},
|
||||||
|
Spec: appsv1.DeploymentSpec{
|
||||||
|
Replicas: int32Ptr(1),
|
||||||
|
Selector: &v1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{
|
||||||
|
"app": "preflight-check",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Template: apiv1.PodTemplateSpec{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "preflight-check",
|
||||||
|
"label": "preflight-check-label",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: apiv1.PodSpec{
|
||||||
|
Containers: []apiv1.Container{
|
||||||
|
{
|
||||||
|
Name: "dep",
|
||||||
|
Image: imageName,
|
||||||
|
Ports: []apiv1.ContainerPort{
|
||||||
|
{
|
||||||
|
Name: "http",
|
||||||
|
Protocol: apiv1.ProtocolTCP,
|
||||||
|
ContainerPort: 80,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Deployment
|
||||||
|
var result *appsv1.Deployment
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
result, err = deploymentsClient.Create(deployment)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
err = fmt.Errorf("unable to create deployments in the %s namespace: %w", namespace, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.LogVerboseMessage("Created deployment %q\n", result.GetObjectMeta().GetName())
|
||||||
|
|
||||||
|
return deployment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) getDeployment(clientset kubernetes.Interface, namespace, depName string) (*appsv1.Deployment, error) {
|
||||||
|
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
||||||
|
var deployment *appsv1.Deployment
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
deployment, err = deploymentsClient.Get(depName, v1.GetOptions{})
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
err = fmt.Errorf("unable to get deployments in the %s namespace: %w", namespace, err)
|
||||||
|
LogDebugMessage("%v\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return deployment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) DeleteDeployment(clientset kubernetes.Interface, namespace, name string) error {
|
||||||
|
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
||||||
|
// Create Deployment
|
||||||
|
deletePolicy := v1.DeletePropagationForeground
|
||||||
|
deleteOptions := v1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePolicy,
|
||||||
|
GracePeriodSeconds: &gracePeriod,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
return deploymentsClient.Delete(name, &deleteOptions)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.WaitForDeploymentToDelete(clientset, namespace, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.LogVerboseMessage("Deleted deployment: %s\n", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) CreatePreflightTestService(clientset kubernetes.Interface, namespace string, svcName string) (*apiv1.Service, error) {
|
||||||
|
iptr := int32Ptr(80)
|
||||||
|
servicesClient := clientset.CoreV1().Services(namespace)
|
||||||
|
service := &apiv1.Service{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: svcName,
|
||||||
|
Namespace: namespace,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "preflight-check",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: apiv1.ServiceSpec{
|
||||||
|
Ports: []apiv1.ServicePort{
|
||||||
|
{Name: "port1",
|
||||||
|
Port: *iptr,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Selector: map[string]string{
|
||||||
|
"app": "preflight-check",
|
||||||
|
},
|
||||||
|
ClusterIP: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var result *apiv1.Service
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
result, err = servicesClient.Create(service)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.LogVerboseMessage("Created service %q\n", result.GetObjectMeta().GetName())
|
||||||
|
|
||||||
|
return service, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) GetService(clientset kubernetes.Interface, namespace, svcName string) (*apiv1.Service, error) {
|
||||||
|
servicesClient := clientset.CoreV1().Services(namespace)
|
||||||
|
var svc *apiv1.Service
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
svc, err = servicesClient.Get(svcName, v1.GetOptions{})
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
err = fmt.Errorf("unable to get services in the %s namespace: %w", namespace, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) DeleteService(clientset kubernetes.Interface, namespace, name string) error {
|
||||||
|
servicesClient := clientset.CoreV1().Services(namespace)
|
||||||
|
// Create Deployment
|
||||||
|
deletePolicy := v1.DeletePropagationForeground
|
||||||
|
deleteOptions := v1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePolicy,
|
||||||
|
}
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
return servicesClient.Delete(name, &deleteOptions)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.LogVerboseMessage("Deleted service: %s\n", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) DeletePod(clientset kubernetes.Interface, namespace, name string) error {
|
||||||
|
|
||||||
|
podsClient := clientset.CoreV1().Pods(namespace)
|
||||||
|
deletePolicy := v1.DeletePropagationForeground
|
||||||
|
deleteOptions := v1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePolicy,
|
||||||
|
GracePeriodSeconds: &gracePeriod,
|
||||||
|
}
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
return podsClient.Delete(name, &deleteOptions)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.waitForPodToDelete(clientset, namespace, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.LogVerboseMessage("Deleted pod: %s\n", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) CreatePreflightTestPod(clientset kubernetes.Interface, namespace, podName, imageName string, secretNames map[string]string, commandToRun []string) (*apiv1.Pod, error) {
|
||||||
|
// build the pod definition we want to deploy
|
||||||
|
pod := &apiv1.Pod{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: podName,
|
||||||
|
Namespace: namespace,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "preflight",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: apiv1.PodSpec{
|
||||||
|
RestartPolicy: apiv1.RestartPolicyNever,
|
||||||
|
Containers: []apiv1.Container{
|
||||||
|
{
|
||||||
|
Name: "cnt",
|
||||||
|
Image: imageName,
|
||||||
|
ImagePullPolicy: apiv1.PullIfNotPresent,
|
||||||
|
Command: commandToRun,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if len(secretNames) > 0 {
|
||||||
|
for secretName, mountPath := range secretNames {
|
||||||
|
pod.Spec.Volumes = append(pod.Spec.Volumes, apiv1.Volume{
|
||||||
|
Name: secretName,
|
||||||
|
VolumeSource: apiv1.VolumeSource{
|
||||||
|
Secret: &apiv1.SecretVolumeSource{
|
||||||
|
SecretName: secretName,
|
||||||
|
Items: []apiv1.KeyToPath{
|
||||||
|
{
|
||||||
|
Key: secretName,
|
||||||
|
Path: filepath.Base(mountPath),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if len(pod.Spec.Containers) > 0 {
|
||||||
|
pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, apiv1.VolumeMount{
|
||||||
|
Name: secretName,
|
||||||
|
MountPath: filepath.Dir(mountPath),
|
||||||
|
ReadOnly: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now create the pod in kubernetes cluster using the clientset
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
pod, err = clientset.CoreV1().Pods(namespace).Create(pod)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.LogVerboseMessage("Created pod: %s\n", pod.Name)
|
||||||
|
return pod, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) getPod(clientset kubernetes.Interface, namespace, podName string) (*apiv1.Pod, error) {
|
||||||
|
LogDebugMessage("Fetching pod: %s\n", podName)
|
||||||
|
var pod *apiv1.Pod
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
pod, err = clientset.CoreV1().Pods(namespace).Get(podName, v1.GetOptions{})
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
LogDebugMessage("%v\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pod, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) GetPodLogs(clientset kubernetes.Interface, pod *apiv1.Pod) (string, error) {
|
||||||
|
return p.GetPodContainerLogs(clientset, pod, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) GetPodContainerLogs(clientset kubernetes.Interface, pod *apiv1.Pod, container string) (string, error) {
|
||||||
|
podLogOpts := apiv1.PodLogOptions{}
|
||||||
|
if container != "" {
|
||||||
|
podLogOpts.Container = container
|
||||||
|
}
|
||||||
|
|
||||||
|
LogDebugMessage("Retrieving logs for pod: %s namespace: %s\n", pod.GetName(), pod.Namespace)
|
||||||
|
req := clientset.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOpts)
|
||||||
|
podLogs, err := req.Stream()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer podLogs.Close()
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err = io.Copy(buf, podLogs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
LogDebugMessage("Log from pod: %s\n", buf.String())
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) waitForResource(checkFunc func() (interface{}, error), validateFunc func(interface{}) bool) error {
|
||||||
|
timeout := time.NewTicker(waitTimeout)
|
||||||
|
defer timeout.Stop()
|
||||||
|
OUT:
|
||||||
|
for {
|
||||||
|
r, err := checkFunc()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-timeout.C:
|
||||||
|
break OUT
|
||||||
|
default:
|
||||||
|
if validateFunc(r) {
|
||||||
|
break OUT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) WaitForDeployment(clientset kubernetes.Interface, namespace string, pfDeployment *appsv1.Deployment) error {
|
||||||
|
var err error
|
||||||
|
depName := pfDeployment.GetName()
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
pfDeployment, err = p.getDeployment(clientset, namespace, depName)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to retrieve deployment: %s\n", depName)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pfDeployment, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(data interface{}) bool {
|
||||||
|
d := data.(*appsv1.Deployment)
|
||||||
|
return int(d.Status.ReadyReplicas) > 0
|
||||||
|
}
|
||||||
|
if err := p.waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if int(pfDeployment.Status.ReadyReplicas) == 0 {
|
||||||
|
err = fmt.Errorf("deployment took longer than expected to spin up pods")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) WaitForPod(clientset kubernetes.Interface, namespace string, pod *apiv1.Pod) error {
|
||||||
|
var err error
|
||||||
|
if len(pod.Spec.Containers) == 0 {
|
||||||
|
err = fmt.Errorf("there are no containers in the pod")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
podName := pod.Name
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
pod, err = p.getPod(clientset, namespace, podName)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to retrieve %s pod by name", podName)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pod, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(data interface{}) bool {
|
||||||
|
po := data.(*apiv1.Pod)
|
||||||
|
return po.Status.Phase == apiv1.PodRunning || po.Status.Phase == apiv1.PodSucceeded || po.Status.Phase == apiv1.PodFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if pod.Status.Phase != apiv1.PodRunning && pod.Status.Phase != apiv1.PodSucceeded && pod.Status.Phase != apiv1.PodFailed {
|
||||||
|
err = fmt.Errorf("container is taking much longer than expected")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) WaitForPodToDie(clientset kubernetes.Interface, namespace string, pod *apiv1.Pod) error {
|
||||||
|
podName := pod.Name
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
po, err := p.getPod(clientset, namespace, podName)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to retrieve %s pod by name", podName)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return po, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(r interface{}) bool {
|
||||||
|
po := r.(*apiv1.Pod)
|
||||||
|
return po.Status.Phase == apiv1.PodFailed || po.Status.Phase == apiv1.PodSucceeded
|
||||||
|
}
|
||||||
|
if err := p.waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) waitForPodToDelete(clientset kubernetes.Interface, namespace, podName string) error {
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
po, err := p.getPod(clientset, namespace, podName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return po, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(po interface{}) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err := p.waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := fmt.Errorf("delete pod is taking unusually long")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) WaitForDeploymentToDelete(clientset kubernetes.Interface, namespace, deploymentName string) error {
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
dep, err := p.getDeployment(clientset, namespace, deploymentName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dep, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(po interface{}) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err := p.waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := fmt.Errorf("delete deployment is taking unusually long")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) CreatePreflightTestSecret(clientset kubernetes.Interface, namespace, secretName string, secretData []byte) (*apiv1.Secret, error) {
|
||||||
|
var secret *apiv1.Secret
|
||||||
|
var err error
|
||||||
|
// build the secret defination we want to create
|
||||||
|
secretSpec := &apiv1.Secret{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: secretName,
|
||||||
|
Namespace: namespace,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "preflight",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
secretName: secretData,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// now create the secret in kubernetes cluster using the clientset
|
||||||
|
if err = p.RetryOnError(func() (err error) {
|
||||||
|
secret, err = clientset.CoreV1().Secrets(namespace).Create(secretSpec)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.LogVerboseMessage("Created Secret: %s\n", secret.Name)
|
||||||
|
return secret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) DeleteK8sSecret(clientset kubernetes.Interface, namespace string, secretName string) error {
|
||||||
|
secretClient := clientset.CoreV1().Secrets(namespace)
|
||||||
|
|
||||||
|
deletePolicy := v1.DeletePropagationForeground
|
||||||
|
deleteOptions := v1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePolicy,
|
||||||
|
}
|
||||||
|
err := secretClient.Delete(secretName, &deleteOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.LogVerboseMessage("Deleted Secret: %s\n", secretName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) CreateStatefulSet(clientset kubernetes.Interface, namespace string, statefulSetName string, imageName string) (*appsv1.StatefulSet, error) {
|
||||||
|
statefulSetsClient := clientset.AppsV1().StatefulSets(namespace)
|
||||||
|
statefulset := &appsv1.StatefulSet{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: statefulSetName,
|
||||||
|
},
|
||||||
|
Spec: appsv1.StatefulSetSpec{
|
||||||
|
Replicas: int32Ptr(1),
|
||||||
|
Selector: &v1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{
|
||||||
|
"app": "postflight-check",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Template: apiv1.PodTemplateSpec{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "postflight-check",
|
||||||
|
"label": "postflight-check-label",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: apiv1.PodSpec{
|
||||||
|
InitContainers: []apiv1.Container{
|
||||||
|
{
|
||||||
|
Name: "migration",
|
||||||
|
Image: "ubuntu",
|
||||||
|
ImagePullPolicy: apiv1.PullIfNotPresent,
|
||||||
|
// Command: []string{"bash", "-c", "for i in {1..10}; do echo \"from init container...\"; sleep 1; done"},
|
||||||
|
Command: []string{"bash", "-c", "for i in {1..10}; do echo \"from init container...\"; sleep 1; exit 1; done"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Containers: []apiv1.Container{
|
||||||
|
{
|
||||||
|
Name: "statefulset",
|
||||||
|
Image: imageName,
|
||||||
|
Ports: []apiv1.ContainerPort{
|
||||||
|
{
|
||||||
|
Name: "http",
|
||||||
|
Protocol: apiv1.ProtocolTCP,
|
||||||
|
ContainerPort: 80,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Statefulset
|
||||||
|
var result *appsv1.StatefulSet
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
result, err = statefulSetsClient.Create(statefulset)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
err = fmt.Errorf("unable to create statefulsets in the %s namespace: %w", namespace, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
LogDebugMessage("Created statefulset %q\n", result.GetObjectMeta().GetName())
|
||||||
|
|
||||||
|
return statefulset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) waitForStatefulSet(clientset kubernetes.Interface, namespace string, pfStatefulset *appsv1.StatefulSet) error {
|
||||||
|
var err error
|
||||||
|
statefulsetName := pfStatefulset.GetName()
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
pfStatefulset, err = p.getStatefulset(clientset, namespace, statefulsetName)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to retrieve stateful set: %s\n", statefulsetName)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pfStatefulset, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(data interface{}) bool {
|
||||||
|
s := data.(*appsv1.StatefulSet)
|
||||||
|
return int(s.Status.ReadyReplicas) > 0
|
||||||
|
}
|
||||||
|
if err := p.waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if int(pfStatefulset.Status.ReadyReplicas) == 0 {
|
||||||
|
err = fmt.Errorf("deployment took longer than expected to spin up pods")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) getStatefulset(clientset kubernetes.Interface, namespace, statefulsetName string) (*appsv1.StatefulSet, error) {
|
||||||
|
statefulsetsClient := clientset.AppsV1().StatefulSets(namespace)
|
||||||
|
var statefulset *appsv1.StatefulSet
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
statefulset, err = statefulsetsClient.Get(statefulsetName, v1.GetOptions{})
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
err = fmt.Errorf("unable to get statefulsets in the %s namespace: %w", namespace, err)
|
||||||
|
fmt.Printf("%v\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return statefulset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) deleteStatefulSet(clientset kubernetes.Interface, namespace, name string) error {
|
||||||
|
statefulsetClient := clientset.AppsV1().StatefulSets(namespace)
|
||||||
|
|
||||||
|
deletePolicy := v1.DeletePropagationForeground
|
||||||
|
deleteOptions := v1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePolicy,
|
||||||
|
GracePeriodSeconds: &gracePeriod,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.RetryOnError(func() (err error) {
|
||||||
|
return statefulsetClient.Delete(name, &deleteOptions)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.waitForStatefulsetToDelete(clientset, namespace, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
LogDebugMessage("Deleted statefulset: %s\n", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) waitForStatefulsetToDelete(clientset kubernetes.Interface, namespace, statefulsetName string) error {
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
statefulset, err := p.getStatefulset(clientset, namespace, statefulsetName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return statefulset, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(po interface{}) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err := p.waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := fmt.Errorf("delete statefulset is taking unusually long")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ClientGoUtils) GetPodsAndPodLogsFromFailedInitContainer(clientset kubernetes.Interface, lbls map[string]string, namespace, containerName string) (map[string]string, error) {
|
||||||
|
set := labels.Set(lbls)
|
||||||
|
listOptions := v1.ListOptions{LabelSelector: set.AsSelector().String()}
|
||||||
|
podList, err := clientset.CoreV1().Pods(namespace).List(listOptions)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to get podlist: %v", err)
|
||||||
|
fmt.Printf("%s\n", err)
|
||||||
|
}
|
||||||
|
LogDebugMessage("%d Pods retrieved\n ", len(podList.Items))
|
||||||
|
|
||||||
|
// var logs map[string]string
|
||||||
|
logs := map[string]string{}
|
||||||
|
for _, pod := range podList.Items {
|
||||||
|
LogDebugMessage("pod: %v\n", pod.GetName())
|
||||||
|
LogDebugMessage("%d init containers retrieved\n", len(pod.Spec.InitContainers))
|
||||||
|
for _, cs := range pod.Status.InitContainerStatuses {
|
||||||
|
if cs.Name == containerName && ((cs.State.Terminated != nil && (cs.State.Terminated.Reason != "Completed" || cs.State.Terminated.ExitCode > 0)) ||
|
||||||
|
(cs.LastTerminationState.Terminated != nil && (cs.LastTerminationState.Terminated.Reason != "Completed" || cs.LastTerminationState.Terminated.ExitCode > 0))) {
|
||||||
|
logs[pod.GetName()], err = p.GetPodContainerLogs(clientset, &pod, cs.Name)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to get pod logs: %v", err)
|
||||||
|
fmt.Printf("%s\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return logs, nil
|
||||||
|
}
|
||||||
1950
pkg/api/clientgo_utils_test.go
Normal file
1950
pkg/api/clientgo_utils_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/qlik-oss/k-apis/pkg/config"
|
"github.com/qlik-oss/k-apis/pkg/config"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
@@ -22,10 +23,9 @@ const (
|
|||||||
QliksenseKind = "Qliksense"
|
QliksenseKind = "Qliksense"
|
||||||
QliksenseGroup = "qlik.com"
|
QliksenseGroup = "qlik.com"
|
||||||
QliksenseDefaultProfile = "docker-desktop"
|
QliksenseDefaultProfile = "docker-desktop"
|
||||||
DefaultRotateKeys = "yes"
|
|
||||||
QliksenseMetadataName = "QliksenseConfigMetadata"
|
QliksenseMetadataName = "QliksenseConfigMetadata"
|
||||||
DefaultMongoDbUri = "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"
|
DefaultMongodbUri = "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"
|
||||||
DefaultMongoDbUriKey = "mongoDbUri"
|
DefaultMongodbUriKey = "mongodbUri"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddCommonConfig adds common configs into CRs
|
// AddCommonConfig adds common configs into CRs
|
||||||
@@ -37,10 +37,9 @@ func (qliksenseCR *QliksenseCR) AddCommonConfig(contextName string) {
|
|||||||
})
|
})
|
||||||
qliksenseCR.SetName(contextName)
|
qliksenseCR.SetName(contextName)
|
||||||
qliksenseCR.Spec = &config.CRSpec{
|
qliksenseCR.Spec = &config.CRSpec{
|
||||||
Profile: QliksenseDefaultProfile,
|
Profile: QliksenseDefaultProfile,
|
||||||
RotateKeys: DefaultRotateKeys,
|
|
||||||
}
|
}
|
||||||
qliksenseCR.Spec.AddToSecrets("qliksense", DefaultMongoDbUriKey, DefaultMongoDbUri, "")
|
qliksenseCR.Spec.AddToSecrets("qliksense", DefaultMongodbUriKey, strings.Replace(DefaultMongodbUri, "qlik-default", contextName, 1), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddBaseQliksenseConfigs adds configs into config.yaml
|
// AddBaseQliksenseConfigs adds configs into config.yaml
|
||||||
@@ -96,7 +95,7 @@ func WriteToFile(content interface{}, targetFile string) error {
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
LogDebugMessage("Wrote content into %s", targetFile)
|
LogDebugMessage("Wrote content into %s\n", targetFile)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/qlik-oss/k-apis/pkg/config"
|
"github.com/qlik-oss/k-apis/pkg/config"
|
||||||
@@ -22,12 +23,11 @@ func TestAddCommonConfig(t *testing.T) {
|
|||||||
q.SetName("myqliksense")
|
q.SetName("myqliksense")
|
||||||
q.SetGroupVersionKind(gvk)
|
q.SetGroupVersionKind(gvk)
|
||||||
q.Spec = &config.CRSpec{
|
q.Spec = &config.CRSpec{
|
||||||
Profile: QliksenseDefaultProfile,
|
Profile: QliksenseDefaultProfile,
|
||||||
RotateKeys: DefaultRotateKeys,
|
|
||||||
Secrets: map[string]config.NameValues{
|
Secrets: map[string]config.NameValues{
|
||||||
"qliksense": []config.NameValue{{
|
"qliksense": []config.NameValue{{
|
||||||
Name: DefaultMongoDbUriKey,
|
Name: DefaultMongodbUriKey,
|
||||||
Value: DefaultMongoDbUri,
|
Value: strings.Replace(DefaultMongodbUri, "qlik-default", "myqliksense", 1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
|
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
|
||||||
@@ -60,7 +61,7 @@ func TestCopyDirectory_withGit_withKuz(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := kapis_git.Checkout(repo2, "v0.0.2", "", nil); err != nil {
|
if err := kapis_git.Checkout(repo2, "v0.0.8", "", nil); err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ func TestCopyDirectory_withGit_withKuz(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := kapis_git.Checkout(repo1, "v0.0.2", "", nil); err != nil {
|
if err := kapis_git.Checkout(repo1, "v0.0.8", "", nil); err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,9 +79,15 @@ func TestCopyDirectory_withGit_withKuz(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(repo2Manifest) != string(repo1Manifest) {
|
re, err := regexp.Compile(`name: qliksense-ca-certificates-[a-z]{5}`)
|
||||||
t.Logf("manifest generated on the original config:\n%v", string(repo1Manifest))
|
if err != nil {
|
||||||
t.Logf("manifest generated on the copied config:\n%v", string(repo2Manifest))
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
repo1ManifestTweaked := re.ReplaceAllString(string(repo1Manifest), "name: qliksense-ca-certificates")
|
||||||
|
repo2ManifestTweaked := re.ReplaceAllString(string(repo2Manifest), "name: qliksense-ca-certificates")
|
||||||
|
if repo2ManifestTweaked != repo1ManifestTweaked {
|
||||||
|
t.Logf("manifest generated on the original config:\n%v", repo1ManifestTweaked)
|
||||||
|
t.Logf("manifest generated on the copied config:\n%v", repo2ManifestTweaked)
|
||||||
t.Fatal("expected manifests to be equal, but they were not")
|
t.Fatal("expected manifests to be equal, but they were not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDockerConfigJsonSecret(t *testing.T) {
|
func TestDockerConfigJsonSecret(t *testing.T) {
|
||||||
@@ -34,10 +34,10 @@ func TestDockerConfigJsonSecret(t *testing.T) {
|
|||||||
t.Fatalf("error unmarshalling yaml string: %v, error: %v", string(dockerConfigJsonSecretYamlBytes), err)
|
t.Fatalf("error unmarshalling yaml string: %v, error: %v", string(dockerConfigJsonSecretYamlBytes), err)
|
||||||
} else if validYamlMap["apiVersion"] != "v1" ||
|
} else if validYamlMap["apiVersion"] != "v1" ||
|
||||||
validYamlMap["kind"] != "Secret" ||
|
validYamlMap["kind"] != "Secret" ||
|
||||||
validYamlMap["metadata"].(map[string]interface{})["name"] != dockerConfigJsonSecret.Name ||
|
validYamlMap["metadata"].(map[interface {}]interface {})["name"] != dockerConfigJsonSecret.Name ||
|
||||||
validYamlMap["type"] != "kubernetes.io/dockerconfigjson" {
|
validYamlMap["type"] != "kubernetes.io/dockerconfigjson" {
|
||||||
t.Fatalf("error verifying validity of secret yaml: %v", string(dockerConfigJsonSecretYamlBytes))
|
t.Fatalf("error verifying validity of secret yaml: %v", string(dockerConfigJsonSecretYamlBytes))
|
||||||
} else if dockerConfigJsonBytesBase64, ok := validYamlMap["data"].(map[string]interface{})[".dockerconfigjson"]; !ok {
|
} else if dockerConfigJsonBytesBase64, ok := validYamlMap["data"].(map[interface {}]interface {})[".dockerconfigjson"]; !ok {
|
||||||
t.Fatalf("no .dockerconfigjson data key in the secret yaml: %v", string(dockerConfigJsonSecretYamlBytes))
|
t.Fatalf("no .dockerconfigjson data key in the secret yaml: %v", string(dockerConfigJsonSecretYamlBytes))
|
||||||
} else if dockerConfigJsonEncryptedBytes, err := base64.StdEncoding.DecodeString(dockerConfigJsonBytesBase64.(string)); err != nil {
|
} else if dockerConfigJsonEncryptedBytes, err := base64.StdEncoding.DecodeString(dockerConfigJsonBytesBase64.(string)); err != nil {
|
||||||
t.Fatalf("error decoding dockerConfigJsonBytes from base64: %v", err)
|
t.Fatalf("error decoding dockerConfigJsonBytes from base64: %v", err)
|
||||||
@@ -45,14 +45,14 @@ func TestDockerConfigJsonSecret(t *testing.T) {
|
|||||||
t.Fatalf("error decrypting dockerConfigJsonBytes: %v", err)
|
t.Fatalf("error decrypting dockerConfigJsonBytes: %v", err)
|
||||||
} else if err := json.Unmarshal(dockerConfigJsonBytes, &dockerConfigJsonMap); err != nil {
|
} else if err := json.Unmarshal(dockerConfigJsonBytes, &dockerConfigJsonMap); err != nil {
|
||||||
t.Fatalf("error unmarshalling dockerConfigJson from json: %v", err)
|
t.Fatalf("error unmarshalling dockerConfigJson from json: %v", err)
|
||||||
} else if dockerConfigJson, ok := dockerConfigJsonMap["auths"].(map[string]interface{})[dockerConfigJsonSecret.Uri]; !ok {
|
} else if dockerConfigJson, ok := dockerConfigJsonMap["auths"].(map[string]interface {})[dockerConfigJsonSecret.Uri]; !ok {
|
||||||
t.Fatalf("dockerConfigJson map does not contain data for the registry: %v", dockerConfigJsonSecret.Uri)
|
t.Fatalf("dockerConfigJson map does not contain data for the registry: %v", dockerConfigJsonSecret.Uri)
|
||||||
} else if dockerConfigJson.(map[string]interface{})["username"] != dockerConfigJsonSecret.Username ||
|
} else if dockerConfigJson.(map[string]interface {})["username"] != dockerConfigJsonSecret.Username ||
|
||||||
dockerConfigJson.(map[string]interface{})["password"] != dockerConfigJsonSecret.Password ||
|
dockerConfigJson.(map[string]interface {})["password"] != dockerConfigJsonSecret.Password ||
|
||||||
dockerConfigJson.(map[string]interface{})["email"] != dockerConfigJsonSecret.Email {
|
dockerConfigJson.(map[string]interface {})["email"] != dockerConfigJsonSecret.Email {
|
||||||
t.Fatal("dockerConfigJson map does not contain expected values")
|
t.Fatal("dockerConfigJson map does not contain expected values")
|
||||||
} else {
|
} else {
|
||||||
authBase64 := dockerConfigJson.(map[string]interface{})["auth"]
|
authBase64 := dockerConfigJson.(map[string]interface {})["auth"]
|
||||||
if auth, err := base64.StdEncoding.DecodeString(authBase64.(string)); err != nil {
|
if auth, err := base64.StdEncoding.DecodeString(authBase64.(string)); err != nil {
|
||||||
t.Fatal("error base64 decoding auth value")
|
t.Fatal("error base64 decoding auth value")
|
||||||
} else if string(auth) != fmt.Sprintf("%s:%s", dockerConfigJsonSecret.Username, dockerConfigJsonSecret.Password) {
|
} else if string(auth) != fmt.Sprintf("%s:%s", dockerConfigJsonSecret.Username, dockerConfigJsonSecret.Password) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KubectlApply create resoruces in the provided namespace,
|
// KubectlApply create resources in the provided namespace,
|
||||||
// if namespace="" then use whatever the kubectl default is
|
// if namespace="" then use whatever the kubectl default is
|
||||||
func KubectlApply(manifests, namespace string) error {
|
func KubectlApply(manifests, namespace string) error {
|
||||||
return kubectlOperation(manifests, "apply", namespace)
|
return kubectlOperation(manifests, "apply", namespace)
|
||||||
@@ -19,7 +19,7 @@ func KubectlApplyVerbose(manifests, namespace string, verbose bool) error {
|
|||||||
return kubectlOperationVerbose(manifests, "apply", namespace, verbose)
|
return kubectlOperationVerbose(manifests, "apply", namespace, verbose)
|
||||||
}
|
}
|
||||||
|
|
||||||
// KubectlDelete delete resoruces in the provided namespace,
|
// KubectlDelete delete resources in the provided namespace,
|
||||||
// if namespace="" then use whatever the kubectl default is
|
// if namespace="" then use whatever the kubectl default is
|
||||||
func KubectlDelete(manifests, namespace string) error {
|
func KubectlDelete(manifests, namespace string) error {
|
||||||
return kubectlOperation(manifests, "delete", namespace)
|
return kubectlOperation(manifests, "delete", namespace)
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ type PreflightConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PreflightSpec struct {
|
type PreflightSpec struct {
|
||||||
MinK8sVersion string `json:"minK8sVersion,omitempty" yaml:"minK8sVersion,omitempty"`
|
MinK8sVersion string `json:"minK8sVersion,omitempty" yaml:"minK8sVersion,omitempty"`
|
||||||
Images map[string]string `json:"images,omitempty" yaml:"images,omitempty"`
|
MinMongoVersion string `json:"minMongoVersion,omitempty" yaml:"minMongoVersion,omitempty"`
|
||||||
|
Images map[string]string `json:"images,omitempty" yaml:"images,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewPreflightConfigEmpty create empty PreflightConfig object
|
//NewPreflightConfigEmpty create empty PreflightConfig object
|
||||||
@@ -74,6 +75,13 @@ func (p *PreflightConfig) AddMinK8sV(version string) {
|
|||||||
p.Spec.MinK8sVersion = version
|
p.Spec.MinK8sVersion = version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PreflightConfig) AddMinMongoV(version string) {
|
||||||
|
if p.Spec == nil {
|
||||||
|
p.Spec = &PreflightSpec{}
|
||||||
|
}
|
||||||
|
p.Spec.MinMongoVersion = version
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PreflightConfig) AddImage(imageFor, imageName string) {
|
func (p *PreflightConfig) AddImage(imageFor, imageName string) {
|
||||||
if p.Spec.Images == nil {
|
if p.Spec.Images == nil {
|
||||||
p.Spec.Images = make(map[string]string)
|
p.Spec.Images = make(map[string]string)
|
||||||
@@ -101,6 +109,11 @@ func (p *PreflightConfig) GetImageName(imageFor string, accountForImageRegistry
|
|||||||
func (p *PreflightConfig) GetMinK8sVersion() string {
|
func (p *PreflightConfig) GetMinK8sVersion() string {
|
||||||
return p.Spec.MinK8sVersion
|
return p.Spec.MinK8sVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PreflightConfig) GetMinMongoVersion() string {
|
||||||
|
return p.Spec.MinMongoVersion
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PreflightConfig) IsExistOnDisk() bool {
|
func (p *PreflightConfig) IsExistOnDisk() bool {
|
||||||
if _, err := os.Lstat(p.GetConfigFilePath()); err != nil {
|
if _, err := os.Lstat(p.GetConfigFilePath()); err != nil {
|
||||||
return false
|
return false
|
||||||
@@ -117,8 +130,9 @@ func (p *PreflightConfig) Initialize() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
p.AddMinK8sV("1.15")
|
p.AddMinK8sV("1.15")
|
||||||
p.AddImage("nginx", "nginx")
|
p.AddMinMongoV("3.6")
|
||||||
p.AddImage("netcat", "subfuzion/netcat")
|
p.AddImage("nginx", "nginx:1.19.0-alpine")
|
||||||
p.AddImage("mongo", "mongo")
|
p.AddImage("netcat", "qlik-docker-oss.bintray.io/preflight-netcat:v1.0.0")
|
||||||
|
p.AddImage("preflight-mongo", "qlik-docker-oss.bintray.io/preflight-mongo:v1.0.0")
|
||||||
return p.Write()
|
return p.Write()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ func checkExists(filename string) os.FileInfo {
|
|||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
LogDebugMessage("File exists")
|
LogDebugMessage("File exists\n")
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,13 +74,14 @@ func ProcessConfigArgs(args []string, base64Encoded bool) ([]*ServiceKeyValue, e
|
|||||||
resultSvcKV := make([]*ServiceKeyValue, len(args))
|
resultSvcKV := make([]*ServiceKeyValue, len(args))
|
||||||
// qliksense.mongodb=somethig
|
// qliksense.mongodb=somethig
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
LogDebugMessage("Arg received: %s", arg)
|
LogDebugMessage("Arg received: %s\n", arg)
|
||||||
first := strings.SplitN(arg, "=", 2)
|
first := strings.SplitN(arg, "=", 2)
|
||||||
if len(first) != 2 {
|
if len(first) != 2 {
|
||||||
return nil, notValidErr
|
return nil, notValidErr
|
||||||
}
|
}
|
||||||
second := strings.SplitN(first[0], ".", 2)
|
|
||||||
if len(second) != 2 {
|
svcKey := getSvcAndKey(first[0])
|
||||||
|
if len(svcKey) != 2 {
|
||||||
return nil, notValidErr
|
return nil, notValidErr
|
||||||
}
|
}
|
||||||
resultValue := strings.Trim(first[1], "\"")
|
resultValue := strings.Trim(first[1], "\"")
|
||||||
@@ -91,14 +93,33 @@ func ProcessConfigArgs(args []string, base64Encoded bool) ([]*ServiceKeyValue, e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
resultSvcKV[i] = &ServiceKeyValue{
|
resultSvcKV[i] = &ServiceKeyValue{
|
||||||
SvcName: second[0],
|
SvcName: svcKey[0],
|
||||||
Key: second[1],
|
Key: svcKey[1],
|
||||||
Value: resultValue,
|
Value: resultValue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return resultSvcKV, nil
|
return resultSvcKV, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// input should be svc[key]
|
||||||
|
func getSvcAndKey(arg string) []string {
|
||||||
|
// for key
|
||||||
|
re := regexp.MustCompile(`\[(.*)\]`)
|
||||||
|
// for service
|
||||||
|
re2 := regexp.MustCompile(`(.*)\[`)
|
||||||
|
|
||||||
|
keys := re.FindStringSubmatch(arg)
|
||||||
|
|
||||||
|
svcs := re2.FindStringSubmatch(arg)
|
||||||
|
if len(svcs) != 2 || len(keys) != 2 {
|
||||||
|
return strings.SplitN(arg, ".", 2)
|
||||||
|
}
|
||||||
|
if svcs[1] == "" || keys[1] == "" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
return []string{svcs[1], keys[1]}
|
||||||
|
}
|
||||||
|
|
||||||
func ExecuteTaskWithBlinkingStdoutFeedback(task func() (interface{}, error), feedback string) (result interface{}, err error) {
|
func ExecuteTaskWithBlinkingStdoutFeedback(task func() (interface{}, error), feedback string) (result interface{}, err error) {
|
||||||
taskDone := make(chan bool)
|
taskDone := make(chan bool)
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@@ -45,3 +45,24 @@ func contains(arr []string, str string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetSvcAndKey(t *testing.T) {
|
||||||
|
s1 := "qliksense[tls.cert]"
|
||||||
|
sa := getSvcAndKey(s1)
|
||||||
|
if sa[0] != "qliksense" || sa[1] != "tls.cert" {
|
||||||
|
t.Fail()
|
||||||
|
t.Logf("expected service: qliksense but got %s", sa[0])
|
||||||
|
t.Logf("expected key: tls.cert but got %s", sa[1])
|
||||||
|
}
|
||||||
|
s1 = "qliksense-idps.tls"
|
||||||
|
sa = getSvcAndKey(s1)
|
||||||
|
for _, s := range sa {
|
||||||
|
t.Logf("|%s|", s)
|
||||||
|
}
|
||||||
|
if sa[0] != "qliksense-idps" || sa[1] != "tls" {
|
||||||
|
t.Fail()
|
||||||
|
t.Logf("expected service: qliksense-idps but got %s", sa[0])
|
||||||
|
t.Logf("expected key: tls but got %s", sa[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
31
pkg/postflight/all_postflight_checks.go
Normal file
31
pkg/postflight/all_postflight_checks.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package postflight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
. "github.com/logrusorgru/aurora"
|
||||||
|
ansi "github.com/mattn/go-colorable"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (qp *QliksensePostflight) RunAllPostflightChecks(namespace string, kubeConfigContents []byte, preflightOpts *PostflightOptions) error {
|
||||||
|
checkCount := 0
|
||||||
|
totalCount := 0
|
||||||
|
|
||||||
|
out := ansi.NewColorableStdout()
|
||||||
|
// Postflight db migration check
|
||||||
|
if err := qp.DbMigrationCheck(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
|
fmt.Printf("Error: %v\n\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||||
|
checkCount++
|
||||||
|
}
|
||||||
|
totalCount++
|
||||||
|
|
||||||
|
if checkCount == totalCount {
|
||||||
|
// All postflight checks were successful
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("1 or more postflight checks have FAILED")
|
||||||
|
}
|
||||||
74
pkg/postflight/db_migration_check.go
Normal file
74
pkg/postflight/db_migration_check.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package postflight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const initContainerNameToCheck = "migration"
|
||||||
|
|
||||||
|
func (p *QliksensePostflight) DbMigrationCheck(namespace string, kubeConfigContents []byte) error {
|
||||||
|
fmt.Printf("Postflight db migration check... \n")
|
||||||
|
p.CG.LogVerboseMessage("\n----------------------------------- \n")
|
||||||
|
clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "")
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to create a kubernetes client: %v", err)
|
||||||
|
fmt.Printf("%s\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var logsMap map[string]string
|
||||||
|
|
||||||
|
// Retrieve all deployments
|
||||||
|
p.CG.LogVerboseMessage("Retrieving logs from deployments\n")
|
||||||
|
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
||||||
|
deployments, err := deploymentsClient.List(v1.ListOptions{})
|
||||||
|
api.LogDebugMessage("Number of deployments found: %d\n", deployments.Size())
|
||||||
|
for _, deployment := range deployments.Items {
|
||||||
|
api.LogDebugMessage("Deployment name: %s\n", deployment.GetName())
|
||||||
|
if logsMap, err = p.CG.GetPodsAndPodLogsFromFailedInitContainer(clientset, deployment.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil {
|
||||||
|
fmt.Printf("%s\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.filterLogsForErrors(logsMap, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieve all statefulsets
|
||||||
|
p.CG.LogVerboseMessage("Retrieving logs from statefulsets\n")
|
||||||
|
statefulsetsClient := clientset.AppsV1().StatefulSets(namespace)
|
||||||
|
statefulsets, err := statefulsetsClient.List(v1.ListOptions{})
|
||||||
|
api.LogDebugMessage("Number of statefulsets found: %d\n", statefulsets.Size())
|
||||||
|
for _, statefulset := range statefulsets.Items {
|
||||||
|
api.LogDebugMessage("Statefulset name: %s\n", statefulset.GetName())
|
||||||
|
if logsMap, err = p.CG.GetPodsAndPodLogsFromFailedInitContainer(clientset, statefulset.Spec.Template.Labels, namespace, initContainerNameToCheck); err != nil {
|
||||||
|
fmt.Printf("%s\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.filterLogsForErrors(logsMap, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *QliksensePostflight) filterLogsForErrors(logsMap map[string]string, namespace string) {
|
||||||
|
errorLogsPresent := false
|
||||||
|
for podName, podLog := range logsMap {
|
||||||
|
containerLogs := strings.Split(podLog, "\n")
|
||||||
|
if len(containerLogs) > 0 {
|
||||||
|
for _, logLine := range containerLogs {
|
||||||
|
if strings.Contains(strings.ToLower(logLine), "error") {
|
||||||
|
errorLogsPresent = true
|
||||||
|
fmt.Printf("Logs from pod: %s\n%s\n", podName, logLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errorLogsPresent {
|
||||||
|
fmt.Printf("To view more logs in this context, please run the command: kubectl logs -n %s %s %s\n", namespace, podName, initContainerNameToCheck)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("no logs obtained\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
pkg/postflight/postflight_utils.go
Normal file
16
pkg/postflight/postflight_utils.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package postflight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PostflightOptions struct {
|
||||||
|
Verbose bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type QliksensePostflight struct {
|
||||||
|
Q *qliksense.Qliksense
|
||||||
|
P *PostflightOptions
|
||||||
|
CG *api.ClientGoUtils
|
||||||
|
}
|
||||||
@@ -3,9 +3,9 @@ package preflight
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
. "github.com/logrusorgru/aurora"
|
||||||
ansi "github.com/mattn/go-colorable"
|
ansi "github.com/mattn/go-colorable"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
. "github.com/logrusorgru/aurora"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions) error {
|
func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions) error {
|
||||||
@@ -15,90 +15,100 @@ func (qp *QliksensePreflight) RunAllPreflightChecks(kubeConfigContents []byte, n
|
|||||||
out := ansi.NewColorableStdout()
|
out := ansi.NewColorableStdout()
|
||||||
// Preflight minimum kuberenetes version check
|
// Preflight minimum kuberenetes version check
|
||||||
if err := qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
|
if err := qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight kubernetes minimum version check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n\n", err)
|
fmt.Printf("Error: %v\n\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "%s\n\n", Green("Preflight kubernetes minimum version check PASSED"))
|
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||||
checkCount++
|
checkCount++
|
||||||
}
|
}
|
||||||
totalCount++
|
totalCount++
|
||||||
|
|
||||||
// Preflight deployment check
|
// Preflight deployment check
|
||||||
if err := qp.CheckDeployment(namespace, kubeConfigContents); err != nil {
|
if err := qp.CheckDeployment(namespace, kubeConfigContents, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight deployment check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n\n", err)
|
fmt.Printf("Error: %v\n\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "%s\n\n", Green("Preflight deployment check PASSED"))
|
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||||
checkCount++
|
checkCount++
|
||||||
}
|
}
|
||||||
totalCount++
|
totalCount++
|
||||||
|
|
||||||
// Preflight service check
|
// Preflight service check
|
||||||
if err := qp.CheckService(namespace, kubeConfigContents); err != nil {
|
if err := qp.CheckService(namespace, kubeConfigContents, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight service check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n\n", err)
|
fmt.Printf("Error: %v\n\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "%s\n\n", Green("Preflight service check PASSED"))
|
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||||
checkCount++
|
checkCount++
|
||||||
}
|
}
|
||||||
totalCount++
|
totalCount++
|
||||||
|
|
||||||
// Preflight pod check
|
// Preflight pod check
|
||||||
if err := qp.CheckPod(namespace, kubeConfigContents); err != nil {
|
if err := qp.CheckPod(namespace, kubeConfigContents, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight pod check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n\n", err)
|
fmt.Printf("Error: %v\n\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "%s\n\n", Green("Preflight pod check PASSED"))
|
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||||
checkCount++
|
checkCount++
|
||||||
}
|
}
|
||||||
totalCount++
|
totalCount++
|
||||||
|
|
||||||
// Preflight role check
|
// Preflight role check
|
||||||
if err := qp.CheckCreateRole(namespace); err != nil {
|
if err := qp.CheckCreateRole(namespace, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red("Preflight role check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n\n", err)
|
fmt.Printf("Error: %v\n\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "%s\n\n", Green("Preflight role check PASSED"))
|
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||||
checkCount++
|
checkCount++
|
||||||
}
|
}
|
||||||
totalCount++
|
totalCount++
|
||||||
|
|
||||||
// Preflight rolebinding check
|
// Preflight rolebinding check
|
||||||
if err := qp.CheckCreateRoleBinding(namespace); err != nil {
|
if err := qp.CheckCreateRoleBinding(namespace, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red(" Preflight rolebinding check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n\n", err)
|
fmt.Printf("Error: %v\n\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "%s\n\n", Green("Preflight rolebinding check PASSED"))
|
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||||
checkCount++
|
checkCount++
|
||||||
}
|
}
|
||||||
totalCount++
|
totalCount++
|
||||||
|
|
||||||
// Preflight serviceaccount check
|
// Preflight serviceaccount check
|
||||||
if err := qp.CheckCreateServiceAccount(namespace); err != nil {
|
if err := qp.CheckCreateServiceAccount(namespace, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red(" Preflight serviceaccount check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n\n", err)
|
fmt.Printf("Error: %v\n\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "%s\n\n", Green("Preflight serviceaccount check PASSED"))
|
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||||
checkCount++
|
checkCount++
|
||||||
}
|
}
|
||||||
totalCount++
|
totalCount++
|
||||||
|
|
||||||
// Preflight mongo check
|
// Preflight mongo check
|
||||||
if err := qp.CheckMongo(kubeConfigContents, namespace, preflightOpts); err != nil {
|
if err := qp.CheckMongo(kubeConfigContents, namespace, preflightOpts, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red(" Preflight mongo check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n\n", err)
|
fmt.Printf("Error: %v\n\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "%s\n\n", Green("Preflight mongo check PASSED"))
|
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||||
checkCount++
|
checkCount++
|
||||||
}
|
}
|
||||||
totalCount++
|
totalCount++
|
||||||
|
|
||||||
// Preflight DNS check
|
// Preflight DNS check
|
||||||
if err := qp.CheckDns(namespace, kubeConfigContents); err != nil {
|
if err := qp.CheckDns(namespace, kubeConfigContents, false); err != nil {
|
||||||
fmt.Fprintf(out, "%s\n", Red(" Preflight DNS check FAILED"))
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
fmt.Printf("Error: %v\n\n", err)
|
fmt.Printf("Error: %v\n\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "%s\n\n", Green("Preflight DNS check PASSED"))
|
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||||
|
checkCount++
|
||||||
|
}
|
||||||
|
totalCount++
|
||||||
|
|
||||||
|
// Preflight verify ca chain check
|
||||||
|
if err := qp.VerifyCAChain(kubeConfigContents, namespace, preflightOpts, false); err != nil {
|
||||||
|
fmt.Fprintf(out, "%s\n", Red("FAILED"))
|
||||||
|
fmt.Printf("Error: %v\n\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(out, "%s\n\n", Green("PASSED"))
|
||||||
checkCount++
|
checkCount++
|
||||||
}
|
}
|
||||||
totalCount++
|
totalCount++
|
||||||
|
|||||||
@@ -6,122 +6,151 @@ import (
|
|||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigContents []byte) error {
|
func (p *QliksensePreflight) CheckDeployment(namespace string, kubeConfigContents []byte, cleanup bool) error {
|
||||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Kube config error: %v\n", err)
|
err = fmt.Errorf("Kube config error: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deployment check
|
// Deployment check
|
||||||
qp.P.LogVerboseMessage("Preflight deployment check: \n")
|
if !cleanup {
|
||||||
qp.P.LogVerboseMessage("--------------------------- \n")
|
fmt.Print("Preflight deployment check... ")
|
||||||
err = qp.checkPfDeployment(clientset, namespace, "deployment-preflight-check")
|
p.CG.LogVerboseMessage("\n--------------------------- \n")
|
||||||
|
}
|
||||||
|
err = p.checkPfDeployment(clientset, namespace, cleanup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
qp.P.LogVerboseMessage("Preflight Deployment check: FAILED\n")
|
p.CG.LogVerboseMessage("Preflight Deployment check: FAILED\n")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Completed preflight deployment check\n")
|
if !cleanup {
|
||||||
|
p.CG.LogVerboseMessage("Completed preflight deployment check\n")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckService(namespace string, kubeConfigContents []byte) error {
|
func (p *QliksensePreflight) CheckService(namespace string, kubeConfigContents []byte, cleanup bool) error {
|
||||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
|
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Service check
|
// Service check
|
||||||
qp.P.LogVerboseMessage("Preflight service check: \n")
|
if !cleanup {
|
||||||
qp.P.LogVerboseMessage("------------------------ \n")
|
fmt.Print("Preflight service check... ")
|
||||||
err = qp.checkPfService(clientset, namespace)
|
p.CG.LogVerboseMessage("\n------------------------ \n")
|
||||||
|
}
|
||||||
|
err = p.checkPfService(clientset, namespace, cleanup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
qp.P.LogVerboseMessage("Preflight Service check: FAILED\n")
|
p.CG.LogVerboseMessage("Preflight Service check: FAILED\n")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Completed preflight service check\n")
|
|
||||||
|
if !cleanup {
|
||||||
|
p.CG.LogVerboseMessage("Completed preflight service check\n")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []byte) error {
|
func (p *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []byte, cleanup bool) error {
|
||||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
|
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Pod check
|
// Pod check
|
||||||
qp.P.LogVerboseMessage("Preflight pod check: \n")
|
if !cleanup {
|
||||||
qp.P.LogVerboseMessage("-------------------- \n")
|
fmt.Print("Preflight pod check... ")
|
||||||
err = qp.checkPfPod(clientset, namespace)
|
p.CG.LogVerboseMessage("\n-------------------- \n")
|
||||||
|
}
|
||||||
|
err = p.checkPfPod(clientset, namespace, cleanup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
qp.P.LogVerboseMessage("Preflight Pod check: FAILED\n")
|
p.CG.LogVerboseMessage("Preflight Pod check: FAILED\n")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Completed preflight pod check\n")
|
if !cleanup {
|
||||||
|
p.CG.LogVerboseMessage("Completed preflight pod check\n")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qp *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namespace string) error {
|
func (p *QliksensePreflight) checkPfPod(clientset kubernetes.Interface, namespace string, cleanup bool) error {
|
||||||
// create a pod
|
// delete the pod we are going to create, if it already exists in the cluster
|
||||||
podName := "pod-pf-check"
|
podName := "pod-pf-check"
|
||||||
|
p.CG.DeletePod(clientset, namespace, podName)
|
||||||
|
if cleanup {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
commandToRun := []string{}
|
commandToRun := []string{}
|
||||||
|
imageName, err := p.GetPreflightConfigObj().GetImageName(nginx, true)
|
||||||
imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pod, err := qp.createPreflightTestPod(clientset, namespace, podName, imageName, nil, commandToRun)
|
// create a pod
|
||||||
|
pod, err := p.CG.CreatePreflightTestPod(clientset, namespace, podName, imageName, nil, commandToRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create pod - %v\n", err)
|
err = fmt.Errorf("unable to create pod - %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer qp.deletePod(clientset, namespace, podName)
|
defer p.CG.DeletePod(clientset, namespace, podName)
|
||||||
|
|
||||||
if err := waitForPod(clientset, namespace, pod); err != nil {
|
if err := p.CG.WaitForPod(clientset, namespace, pod); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
qp.P.LogVerboseMessage("Preflight pod creation check: PASSED\n")
|
p.CG.LogVerboseMessage("Preflight pod creation check: PASSED\n")
|
||||||
qp.P.LogVerboseMessage("Cleaning up resources...\n")
|
p.CG.LogVerboseMessage("Cleaning up resources...\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qp *QliksensePreflight) checkPfService(clientset *kubernetes.Clientset, namespace string) error {
|
func (p *QliksensePreflight) checkPfService(clientset kubernetes.Interface, namespace string, cleanup bool) error {
|
||||||
// creating service
|
// delete the service we are going to create, if it already exists in the cluster
|
||||||
serviceName := "svc-pf-check"
|
serviceName := "svc-pf-check"
|
||||||
pfService, err := qp.createPreflightTestService(clientset, namespace, serviceName)
|
p.CG.DeleteService(clientset, namespace, serviceName)
|
||||||
|
if cleanup {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// creating service
|
||||||
|
pfService, err := p.CG.CreatePreflightTestService(clientset, namespace, serviceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create service - %v\n", err)
|
err = fmt.Errorf("unable to create service - %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer qp.deleteService(clientset, namespace, serviceName)
|
defer p.CG.DeleteService(clientset, namespace, serviceName)
|
||||||
_, err = getService(clientset, namespace, pfService.GetName())
|
_, err = p.CG.GetService(clientset, namespace, pfService.GetName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to retrieve service - %v\n", err)
|
err = fmt.Errorf("unable to retrieve service - %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Preflight service creation check: PASSED\n")
|
p.CG.LogVerboseMessage("Preflight service creation check: PASSED\n")
|
||||||
qp.P.LogVerboseMessage("Cleaning up resources...\n")
|
p.CG.LogVerboseMessage("Cleaning up resources...\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qp *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset, namespace, depName string) error {
|
func (p *QliksensePreflight) checkPfDeployment(clientset kubernetes.Interface, namespace string, cleanup bool) error {
|
||||||
|
// delete the deployment we are going to create, if it already exists in the cluster
|
||||||
|
depName := "deployment-preflight-check"
|
||||||
|
p.CG.DeleteDeployment(clientset, namespace, depName)
|
||||||
|
if cleanup {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// check if we are able to create a deployment
|
// check if we are able to create a deployment
|
||||||
imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
|
imageName, err := p.GetPreflightConfigObj().GetImageName(nginx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pfDeployment, err := qp.createPreflightTestDeployment(clientset, namespace, depName, imageName)
|
pfDeployment, err := p.CG.CreatePreflightTestDeployment(clientset, namespace, depName, imageName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create deployment - %v\n", err)
|
err = fmt.Errorf("unable to create deployment - %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer qp.deleteDeployment(clientset, namespace, depName)
|
defer p.CG.DeleteDeployment(clientset, namespace, depName)
|
||||||
if err := waitForDeployment(clientset, namespace, pfDeployment); err != nil {
|
if err := p.CG.WaitForDeployment(clientset, namespace, pfDeployment); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Preflight Deployment check: PASSED\n")
|
p.CG.LogVerboseMessage("Preflight Deployment check: PASSED\n")
|
||||||
qp.P.LogVerboseMessage("Cleaning up resources...\n")
|
p.CG.LogVerboseMessage("Cleaning up resources...\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package preflight
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -10,58 +12,68 @@ const (
|
|||||||
netcat = "netcat"
|
netcat = "netcat"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byte) error {
|
func (p *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byte, cleanup bool) error {
|
||||||
qp.P.LogVerboseMessage("Preflight DNS check: \n")
|
depName := "dep-dns-preflight-check"
|
||||||
qp.P.LogVerboseMessage("------------------- \n")
|
serviceName := "svc-dns-pf-check"
|
||||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
podName := "pf-pod-1"
|
||||||
|
|
||||||
|
if !cleanup {
|
||||||
|
fmt.Print("Preflight DNS check... ")
|
||||||
|
p.CG.LogVerboseMessage("\n------------------- \n")
|
||||||
|
}
|
||||||
|
clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
|
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete the deployment we are going to create, if it already exists in the cluster
|
||||||
|
p.runDNSCleanup(clientset, namespace, podName, serviceName, depName)
|
||||||
|
if cleanup {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
// creating deployment
|
// creating deployment
|
||||||
depName := "dep-dns-preflight-check"
|
nginxImageName, err := p.GetPreflightConfigObj().GetImageName(nginx, true)
|
||||||
nginxImageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dnsDeployment, err := qp.createPreflightTestDeployment(clientset, namespace, depName, nginxImageName)
|
|
||||||
|
dnsDeployment, err := p.CG.CreatePreflightTestDeployment(clientset, namespace, depName, nginxImageName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create deployment: %v\n", err)
|
err = fmt.Errorf("unable to create deployment: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer qp.deleteDeployment(clientset, namespace, depName)
|
defer p.CG.DeleteDeployment(clientset, namespace, depName)
|
||||||
|
|
||||||
if err := waitForDeployment(clientset, namespace, dnsDeployment); err != nil {
|
if err := p.CG.WaitForDeployment(clientset, namespace, dnsDeployment); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// creating service
|
// creating service
|
||||||
serviceName := "svc-dns-pf-check"
|
dnsService, err := p.CG.CreatePreflightTestService(clientset, namespace, serviceName)
|
||||||
dnsService, err := qp.createPreflightTestService(clientset, namespace, serviceName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create service : %s, %s\n", serviceName, err)
|
err = fmt.Errorf("unable to create service : %s, %s\n", serviceName, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer qp.deleteService(clientset, namespace, serviceName)
|
defer p.CG.DeleteService(clientset, namespace, serviceName)
|
||||||
|
|
||||||
// create a pod
|
// create a pod
|
||||||
podName := "pf-pod-1"
|
|
||||||
commandToRun := []string{"sh", "-c", "sleep 10; nc -z -v -w 1 " + dnsService.Name + " 80"}
|
commandToRun := []string{"sh", "-c", "sleep 10; nc -z -v -w 1 " + dnsService.Name + " 80"}
|
||||||
netcatImageName, err := qp.GetPreflightConfigObj().GetImageName(netcat, true)
|
netcatImageName, err := p.GetPreflightConfigObj().GetImageName(netcat, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to retrieve image : %v\n", err)
|
err = fmt.Errorf("unable to retrieve image : %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dnsPod, err := qp.createPreflightTestPod(clientset, namespace, podName, netcatImageName, nil, commandToRun)
|
|
||||||
|
dnsPod, err := p.CG.CreatePreflightTestPod(clientset, namespace, podName, netcatImageName, nil, commandToRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create pod : %s, %s\n", podName, err)
|
err = fmt.Errorf("unable to create pod : %s, %s\n", podName, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer qp.deletePod(clientset, namespace, podName)
|
defer p.CG.DeletePod(clientset, namespace, podName)
|
||||||
|
|
||||||
if err := waitForPod(clientset, namespace, dnsPod); err != nil {
|
if err := p.CG.WaitForPod(clientset, namespace, dnsPod); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(dnsPod.Spec.Containers) == 0 {
|
if len(dnsPod.Spec.Containers) == 0 {
|
||||||
@@ -69,23 +81,30 @@ func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []by
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForPodToDie(clientset, namespace, dnsPod)
|
p.CG.WaitForPodToDie(clientset, namespace, dnsPod)
|
||||||
|
|
||||||
logStr, err := getPodLogs(clientset, dnsPod)
|
logStr, err := p.CG.GetPodLogs(clientset, dnsPod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to execute dns check in the cluster: %v", err)
|
err = fmt.Errorf("unable to execute dns check in the cluster: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(strings.TrimSpace(logStr), "succeeded!") {
|
if strings.HasSuffix(strings.TrimSpace(logStr), "succeeded!") {
|
||||||
qp.P.LogVerboseMessage("Preflight DNS check: PASSED\n")
|
p.CG.LogVerboseMessage("Preflight DNS check: PASSED\n")
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("Expected response not found\n")
|
err = fmt.Errorf("Expected response not found\n")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if !cleanup {
|
||||||
qp.P.LogVerboseMessage("Completed preflight DNS check\n")
|
p.CG.LogVerboseMessage("Completed preflight DNS check\n")
|
||||||
qp.P.LogVerboseMessage("Cleaning up resources...\n")
|
p.CG.LogVerboseMessage("Cleaning up resources...\n")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *QliksensePreflight) runDNSCleanup(clientset kubernetes.Interface, namespace, podName, serviceName, depName string) {
|
||||||
|
p.CG.DeleteDeployment(clientset, namespace, depName)
|
||||||
|
p.CG.DeletePod(clientset, namespace, podName)
|
||||||
|
p.CG.DeleteService(clientset, namespace, serviceName)
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ package preflight
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
@@ -12,131 +16,133 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
mongo = "mongo"
|
preflight_mongo = "preflight-mongo"
|
||||||
|
caCertMountPath = "/etc/ssl/certs/ca-certificates.crt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions) error {
|
func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error {
|
||||||
qp.P.LogVerboseMessage("Preflight mongodb check: \n")
|
if !cleanup {
|
||||||
qp.P.LogVerboseMessage("------------------------ \n")
|
fmt.Print("Preflight mongodb check... ")
|
||||||
|
qp.CG.LogVerboseMessage("\n------------------------ \n")
|
||||||
if preflightOpts.MongoOptions.MongodbUrl == "" {
|
|
||||||
// infer mongoDbUrl from currentCR
|
|
||||||
qp.P.LogVerboseMessage("MongoDbUri is empty, infer from CR\n")
|
|
||||||
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
|
|
||||||
var currentCR *qapi.QliksenseCR
|
|
||||||
|
|
||||||
var err error
|
|
||||||
qConfig.SetNamespace(namespace)
|
|
||||||
currentCR, err = qConfig.GetCurrentCR()
|
|
||||||
if err != nil {
|
|
||||||
qp.P.LogVerboseMessage("Unable to retrieve current CR: %v\n", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
decryptedCR, err := qConfig.GetDecryptedCr(currentCR)
|
|
||||||
if err != nil {
|
|
||||||
qp.P.LogVerboseMessage("An error occurred while retrieving mongodbUrl from current CR: %v\n", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
preflightOpts.MongoOptions.MongodbUrl = decryptedCR.Spec.GetFromSecrets("qliksense", "mongoDbUri")
|
|
||||||
}
|
}
|
||||||
|
var currentCR *qapi.QliksenseCR
|
||||||
qp.P.LogVerboseMessage("MongodbUrl: %s\n", preflightOpts.MongoOptions.MongodbUrl)
|
var err error
|
||||||
if err := qp.mongoConnCheck(kubeConfigContents, namespace, preflightOpts); err != nil {
|
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
|
||||||
|
qConfig.SetNamespace(namespace)
|
||||||
|
currentCR, err = qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
qp.CG.LogVerboseMessage("Unable to retrieve current CR: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Completed preflight mongodb check\n")
|
decryptedCR, err := qConfig.GetDecryptedCr(currentCR)
|
||||||
|
if err != nil {
|
||||||
|
qp.CG.LogVerboseMessage("An error occurred while retrieving mongodbUrl from current CR: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if preflightOpts.MongoOptions.MongodbUrl == "" && !cleanup {
|
||||||
|
// infer mongoDbUrl from currentCR
|
||||||
|
qp.CG.LogVerboseMessage("mongodbUri is empty, infer from CR\n")
|
||||||
|
preflightOpts.MongoOptions.MongodbUrl = strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("qliksense", "mongodbUri"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if preflightOpts.MongoOptions.CaCertFile == "" && !cleanup {
|
||||||
|
caCertStr := decryptedCR.Spec.GetFromSecrets("qliksense", "caCertificates")
|
||||||
|
tmpDir := os.TempDir()
|
||||||
|
caCrtFile := filepath.Join(tmpDir, "rootCA.crt")
|
||||||
|
api.LogDebugMessage("received ca crt: %s\n", caCertStr)
|
||||||
|
if err := ioutil.WriteFile(caCrtFile, []byte(caCertStr), 0644); err != nil {
|
||||||
|
return fmt.Errorf("unable to write CA crt to file: %v", err)
|
||||||
|
}
|
||||||
|
preflightOpts.MongoOptions.CaCertFile = caCrtFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cleanup {
|
||||||
|
qp.CG.LogVerboseMessage("MongodbUrl: %s\n", preflightOpts.MongoOptions.MongodbUrl)
|
||||||
|
|
||||||
|
// if mongodbUrl is empty, abort check
|
||||||
|
if preflightOpts.MongoOptions.MongodbUrl == "" {
|
||||||
|
qp.CG.LogVerboseMessage("Mongodb Url is empty, hence aborting preflight check\n")
|
||||||
|
return errors.New("MongodbUrl is empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := qp.mongoConnCheck(kubeConfigContents, namespace, preflightOpts, cleanup); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cleanup {
|
||||||
|
qp.CG.LogVerboseMessage("Completed preflight mongodb check\n")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions) error {
|
func (p *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error {
|
||||||
var caCertSecretName, clientCertSecretName string
|
caCertSecretName := "ca-certificates-crt"
|
||||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
mongoPodName := "pf-mongo-pod"
|
||||||
|
clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
|
err = fmt.Errorf("unable to create a kubernetes client: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var secrets []string
|
|
||||||
|
// cleanup before starting check
|
||||||
|
p.runMongoCleanup(clientset, namespace, mongoPodName, caCertSecretName)
|
||||||
|
if cleanup {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
secrets := map[string]string{}
|
||||||
if preflightOpts.MongoOptions.CaCertFile != "" {
|
if preflightOpts.MongoOptions.CaCertFile != "" {
|
||||||
caCertSecretName = "preflight-mongo-test-cacert"
|
caCertSecret, err := p.createSecret(clientset, namespace, preflightOpts.MongoOptions.CaCertFile, caCertSecretName)
|
||||||
caCertSecret, err := qp.createSecret(clientset, namespace, preflightOpts.MongoOptions.CaCertFile, caCertSecretName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create a ca cert kubernetes secret: %v\n", err)
|
err = fmt.Errorf("unable to create a ca cert kubernetes secret: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer qp.deleteK8sSecret(clientset, namespace, caCertSecret)
|
defer p.CG.DeleteK8sSecret(clientset, namespace, caCertSecret.Name)
|
||||||
secrets = append(secrets, caCertSecretName)
|
secrets[caCertSecretName] = caCertMountPath
|
||||||
}
|
|
||||||
if preflightOpts.MongoOptions.ClientCertFile != "" {
|
|
||||||
clientCertSecretName = "preflight-mongo-test-clientcert"
|
|
||||||
clientCertSecret, err := qp.createSecret(clientset, namespace, preflightOpts.MongoOptions.ClientCertFile, clientCertSecretName)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("unable to create a client cert kubernetes secret: %v\n", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer qp.deleteK8sSecret(clientset, namespace, clientCertSecret)
|
|
||||||
secrets = append(secrets, clientCertSecretName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mongoCommand := strings.Builder{}
|
commandToRun := []string{"./preflight-mongo", fmt.Sprintf(`-url="%s"`, preflightOpts.MongoOptions.MongodbUrl)}
|
||||||
mongoCommand.WriteString(fmt.Sprintf("sleep 10;mongo %s", preflightOpts.MongoOptions.MongodbUrl))
|
|
||||||
if preflightOpts.MongoOptions.Username != "" {
|
|
||||||
mongoCommand.WriteString(fmt.Sprintf(" --username %s", preflightOpts.MongoOptions.Username))
|
|
||||||
api.LogDebugMessage("Adding username: Mongo command: %s\n", mongoCommand.String())
|
|
||||||
}
|
|
||||||
if preflightOpts.MongoOptions.Password != "" {
|
|
||||||
mongoCommand.WriteString(fmt.Sprintf(" --password %s", preflightOpts.MongoOptions.Password))
|
|
||||||
api.LogDebugMessage("Adding username and password\n")
|
|
||||||
}
|
|
||||||
if preflightOpts.MongoOptions.Tls || preflightOpts.MongoOptions.CaCertFile != "" || preflightOpts.MongoOptions.ClientCertFile != "" {
|
|
||||||
mongoCommand.WriteString(" --tls")
|
|
||||||
api.LogDebugMessage("Adding --tls: Mongo command: %s\n", mongoCommand.String())
|
|
||||||
}
|
|
||||||
if preflightOpts.MongoOptions.CaCertFile != "" {
|
|
||||||
mongoCommand.WriteString(fmt.Sprintf(" --tlsCAFile=/etc/ssl/%s/%[1]s", caCertSecretName))
|
|
||||||
api.LogDebugMessage("Adding caCertFile: Mongo command: %s\n", mongoCommand.String())
|
|
||||||
}
|
|
||||||
if preflightOpts.MongoOptions.ClientCertFile != "" {
|
|
||||||
mongoCommand.WriteString(fmt.Sprintf(" --tlsCertificateKeyFile=/etc/ssl/%s/%[1]s", clientCertSecretName))
|
|
||||||
api.LogDebugMessage("Adding clientCertFile: Mongo command: %s\n", mongoCommand.String())
|
|
||||||
}
|
|
||||||
mongoCommand.WriteString(` --eval "print(\"connected to mongo\")"`)
|
|
||||||
|
|
||||||
commandToRun := []string{"sh", "-c", mongoCommand.String()}
|
|
||||||
api.LogDebugMessage("Mongo command: %s\n", strings.Join(commandToRun, " "))
|
api.LogDebugMessage("Mongo command: %s\n", strings.Join(commandToRun, " "))
|
||||||
|
|
||||||
// create a pod
|
// create a pod
|
||||||
podName := "pf-mongo-pod"
|
imageName, err := p.GetPreflightConfigObj().GetImageName(preflight_mongo, true)
|
||||||
imageName, err := qp.GetPreflightConfigObj().GetImageName(mongo, true)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to retrieve image : %v\n", err)
|
err = fmt.Errorf("unable to retrieve image : %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mongoPod, err := qp.createPreflightTestPod(clientset, namespace, podName, imageName, secrets, commandToRun)
|
api.LogDebugMessage("image name to be used: %s\n", imageName)
|
||||||
|
mongoPod, err := p.CG.CreatePreflightTestPod(clientset, namespace, mongoPodName, imageName, secrets, commandToRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create pod : %v\n", err)
|
err = fmt.Errorf("unable to create pod : %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer qp.deletePod(clientset, namespace, podName)
|
defer p.CG.DeletePod(clientset, namespace, mongoPodName)
|
||||||
|
|
||||||
if err := waitForPod(clientset, namespace, mongoPod); err != nil {
|
if err := p.CG.WaitForPod(clientset, namespace, mongoPod); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(mongoPod.Spec.Containers) == 0 {
|
if len(mongoPod.Spec.Containers) == 0 {
|
||||||
err := fmt.Errorf("there are no containers in the pod- %v\n", err)
|
err := fmt.Errorf("there are no containers in the pod- %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
waitForPodToDie(clientset, namespace, mongoPod)
|
p.CG.WaitForPodToDie(clientset, namespace, mongoPod)
|
||||||
logStr, err := getPodLogs(clientset, mongoPod)
|
logStr, err := p.CG.GetPodLogs(clientset, mongoPod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to execute mongo check in the cluster: %v\n", err)
|
err = fmt.Errorf("unable to execute mongo check in the cluster: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stringToCheck := "Implicit session:"
|
// check mongo server version
|
||||||
|
ok, err := p.checkMongoVersion(logStr)
|
||||||
|
if !ok || err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if connection succeeded
|
||||||
|
stringToCheck := "qlik - connection succeeded!!"
|
||||||
if strings.Contains(logStr, stringToCheck) {
|
if strings.Contains(logStr, stringToCheck) {
|
||||||
qp.P.LogVerboseMessage("Preflight mongo check: PASSED\n")
|
p.CG.LogVerboseMessage("Preflight mongo check: PASSED\n")
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("Connection failed: %s\n", logStr)
|
err = fmt.Errorf("Connection failed: %s\n", logStr)
|
||||||
return err
|
return err
|
||||||
@@ -144,16 +150,58 @@ func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespac
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qp *QliksensePreflight) createSecret(clientset *kubernetes.Clientset, namespace, certFile, certSecretName string) (*apiv1.Secret, error) {
|
func (p *QliksensePreflight) checkMongoVersion(logStr string) (bool, error) {
|
||||||
|
// check mongo server version
|
||||||
|
api.LogDebugMessage("Minimum required mongo version: %s\n", p.GetPreflightConfigObj().GetMinMongoVersion())
|
||||||
|
mongoVersionStrToCheck := "qlik mongo server version:"
|
||||||
|
if strings.Contains(logStr, mongoVersionStrToCheck) {
|
||||||
|
logLines := strings.Split(logStr, "\n")
|
||||||
|
for _, eachline := range logLines {
|
||||||
|
if strings.Contains(eachline, mongoVersionStrToCheck) {
|
||||||
|
mongoVersionLog := strings.Split(eachline, ":")
|
||||||
|
if len(mongoVersionLog) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mongoVersionStr := strings.ReplaceAll(strings.TrimSpace(mongoVersionLog[1]), `"`, "")
|
||||||
|
api.LogDebugMessage("Extracted mongo version from pod log: %s\n", mongoVersionStr)
|
||||||
|
currentMongoVersionSemver, err := semver.NewVersion(mongoVersionStr)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to convert minimum mongo version into semver version:%v\n", err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
minMongoVersionSemver, err := semver.NewVersion(p.GetPreflightConfigObj().GetMinMongoVersion())
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to convert required minimum mongo version into semver version:%v\n", err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if currentMongoVersionSemver.GreaterThan(minMongoVersionSemver) || currentMongoVersionSemver.Equal(minMongoVersionSemver) {
|
||||||
|
p.CG.LogVerboseMessage("Current mongodb server version %s is greater than or equal to minimum required mongodb version: %s\n", currentMongoVersionSemver, minMongoVersionSemver)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("Current mongodb server version %s is less than minimum required mongodb version: %s", currentMongoVersionSemver, minMongoVersionSemver)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := errors.New("Unable to infer mongodb server version")
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *QliksensePreflight) createSecret(clientset kubernetes.Interface, namespace, certFile, certSecretName string) (*apiv1.Secret, error) {
|
||||||
certBytes, err := ioutil.ReadFile(certFile)
|
certBytes, err := ioutil.ReadFile(certFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
certSecret, err := qp.createPreflightTestSecret(clientset, namespace, certSecretName, certBytes)
|
certSecret, err := p.CG.CreatePreflightTestSecret(clientset, namespace, certSecretName, certBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to create secret with ca cert : %v\n", err)
|
err = fmt.Errorf("unable to create secret with cert : %v\n", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return certSecret, nil
|
return certSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *QliksensePreflight) runMongoCleanup(clientset kubernetes.Interface, namespace, mongoPodName, caCertSecretName string) {
|
||||||
|
p.CG.DeletePod(clientset, namespace, mongoPodName)
|
||||||
|
p.CG.DeleteK8sSecret(clientset, namespace, caCertSecretName)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,30 +1,8 @@
|
|||||||
package preflight
|
package preflight
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/mitchellh/go-homedir"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
|
||||||
apiv1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/api/rbac/v1beta1"
|
|
||||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/client-go/rest"
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
"k8s.io/client-go/util/retry"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PreflightOptions struct {
|
type PreflightOptions struct {
|
||||||
@@ -32,683 +10,42 @@ type PreflightOptions struct {
|
|||||||
MongoOptions *MongoOptions
|
MongoOptions *MongoOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogVerboseMessage logs a verbose message
|
|
||||||
func (p *PreflightOptions) LogVerboseMessage(strMessage string, args ...interface{}) {
|
|
||||||
if p.Verbose || os.Getenv("QLIKSENSE_DEBUG") == "true" {
|
|
||||||
fmt.Printf(strMessage, args...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type MongoOptions struct {
|
type MongoOptions struct {
|
||||||
MongodbUrl string
|
MongodbUrl string
|
||||||
Username string
|
CaCertFile string
|
||||||
Password string
|
|
||||||
CaCertFile string
|
|
||||||
ClientCertFile string
|
|
||||||
Tls bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var gracePeriod int64 = 0
|
|
||||||
|
|
||||||
type QliksensePreflight struct {
|
type QliksensePreflight struct {
|
||||||
Q *qliksense.Qliksense
|
Q *qliksense.Qliksense
|
||||||
P *PreflightOptions
|
P *PreflightOptions
|
||||||
|
CG *api.ClientGoUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qp *QliksensePreflight) GetPreflightConfigObj() *api.PreflightConfig {
|
func (qp *QliksensePreflight) GetPreflightConfigObj() *api.PreflightConfig {
|
||||||
return api.NewPreflightConfig(qp.Q.QliksenseHome)
|
return api.NewPreflightConfig(qp.Q.QliksenseHome)
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitPreflight() (string, []byte, error) {
|
func (qp *QliksensePreflight) Cleanup(namespace string, kubeConfigContents []byte) error {
|
||||||
api.LogDebugMessage("Reading .kube/config file...")
|
qp.CG.LogVerboseMessage("Preflight clean\n")
|
||||||
|
qp.CG.LogVerboseMessage("----------------\n")
|
||||||
|
|
||||||
homeDir, err := homedir.Dir()
|
qp.CG.LogVerboseMessage("Removing deployment...\n")
|
||||||
if err != nil {
|
qp.CheckDeployment(namespace, kubeConfigContents, true)
|
||||||
err = fmt.Errorf("Unable to deduce home dir\n")
|
qp.CG.LogVerboseMessage("Removing service...\n")
|
||||||
return "", nil, err
|
qp.CheckService(namespace, kubeConfigContents, true)
|
||||||
}
|
qp.CG.LogVerboseMessage("Removing pod...\n")
|
||||||
api.LogDebugMessage("Kube config location: %s\n\n", filepath.Join(homeDir, ".kube", "config"))
|
qp.CheckPod(namespace, kubeConfigContents, true)
|
||||||
|
|
||||||
kubeConfig := filepath.Join(homeDir, ".kube", "config")
|
qp.CG.LogVerboseMessage("Removing role...\n")
|
||||||
kubeConfigContents, err := ioutil.ReadFile(kubeConfig)
|
qp.CheckCreateRole(namespace, true)
|
||||||
if err != nil {
|
qp.CG.LogVerboseMessage("Removing rolebinding...\n")
|
||||||
err = fmt.Errorf("Unable to deduce home dir\n")
|
qp.CheckCreateRoleBinding(namespace, true)
|
||||||
return "", nil, err
|
qp.CG.LogVerboseMessage("Removing serviceaccount...\n")
|
||||||
}
|
qp.CheckCreateServiceAccount(namespace, true)
|
||||||
// retrieve namespace
|
|
||||||
namespace := api.GetKubectlNamespace()
|
|
||||||
// if namespace comes back empty, we will run checks in the default namespace
|
|
||||||
if namespace == "" {
|
|
||||||
namespace = "default"
|
|
||||||
}
|
|
||||||
api.LogDebugMessage("Namespace: %s\n", namespace)
|
|
||||||
return namespace, kubeConfigContents, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initiateK8sOps(opr, namespace string) error {
|
qp.CG.LogVerboseMessage("Removing DNS check components...\n")
|
||||||
opr1 := strings.Fields(opr)
|
qp.CheckDns(namespace, kubeConfigContents, true)
|
||||||
_, err := api.KubectlDirectOps(opr1, namespace)
|
qp.CG.LogVerboseMessage("Removing mongo check components...\n")
|
||||||
if err != nil {
|
qp.CheckMongo(kubeConfigContents, namespace, &PreflightOptions{MongoOptions: &MongoOptions{}}, true)
|
||||||
fmt.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func int32Ptr(i int32) *int32 { return &i }
|
|
||||||
|
|
||||||
func retryOnError(mf func() error) error {
|
|
||||||
return retry.OnError(wait.Backoff{
|
|
||||||
Duration: 1 * time.Second,
|
|
||||||
Factor: 1,
|
|
||||||
Jitter: 0.1,
|
|
||||||
Steps: 5,
|
|
||||||
}, func(err error) bool {
|
|
||||||
return k8serrors.IsConflict(err) || k8serrors.IsGone(err) || k8serrors.IsServerTimeout(err) ||
|
|
||||||
k8serrors.IsServiceUnavailable(err) || k8serrors.IsTimeout(err) || k8serrors.IsTooManyRequests(err)
|
|
||||||
}, mf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clientset, *rest.Config, error) {
|
|
||||||
var clientConfig *rest.Config
|
|
||||||
var err error
|
|
||||||
if len(kubeconfig) == 0 {
|
|
||||||
clientConfig, err = rest.InClusterConfig()
|
|
||||||
if err != nil {
|
|
||||||
err = errors.Wrap(err, "Unable to load in-cluster kubeconfig")
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
config, err := clientcmd.Load(kubeconfig)
|
|
||||||
if err != nil {
|
|
||||||
err = errors.Wrap(err, "Unable to load kubeconfig")
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if contextName != "" {
|
|
||||||
config.CurrentContext = contextName
|
|
||||||
}
|
|
||||||
clientConfig, err = clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig()
|
|
||||||
if err != nil {
|
|
||||||
err = errors.Wrap(err, "Unable to create client config from config")
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clientset, err := kubernetes.NewForConfig(clientConfig)
|
|
||||||
if err != nil {
|
|
||||||
err = errors.Wrap(err, "Unable to create clientset")
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return clientset, clientConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) createPreflightTestDeployment(clientset *kubernetes.Clientset, namespace string, depName string, imageName string) (*appsv1.Deployment, error) {
|
|
||||||
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
|
||||||
deployment := &appsv1.Deployment{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: depName,
|
|
||||||
},
|
|
||||||
Spec: appsv1.DeploymentSpec{
|
|
||||||
Replicas: int32Ptr(1),
|
|
||||||
Selector: &v1.LabelSelector{
|
|
||||||
MatchLabels: map[string]string{
|
|
||||||
"app": "preflight-check",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Template: apiv1.PodTemplateSpec{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Labels: map[string]string{
|
|
||||||
"app": "preflight-check",
|
|
||||||
"label": "preflight-check-label",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Spec: apiv1.PodSpec{
|
|
||||||
Containers: []apiv1.Container{
|
|
||||||
{
|
|
||||||
Name: "dep",
|
|
||||||
Image: imageName,
|
|
||||||
Ports: []apiv1.ContainerPort{
|
|
||||||
{
|
|
||||||
Name: "http",
|
|
||||||
Protocol: apiv1.ProtocolTCP,
|
|
||||||
ContainerPort: 80,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Deployment
|
|
||||||
var result *appsv1.Deployment
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
result, err = deploymentsClient.Create(deployment)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
err = errors.Wrapf(err, "unable to create deployments in the %s namespace", namespace)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Created deployment %q\n", result.GetObjectMeta().GetName())
|
|
||||||
|
|
||||||
return deployment, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDeployment(clientset *kubernetes.Clientset, namespace, depName string) (*appsv1.Deployment, error) {
|
|
||||||
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
|
||||||
var deployment *appsv1.Deployment
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
deployment, err = deploymentsClient.Get(depName, v1.GetOptions{})
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
err = errors.Wrapf(err, "unable to get deployments in the %s namespace", namespace)
|
|
||||||
api.LogDebugMessage("%v\n", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return deployment, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) deleteDeployment(clientset *kubernetes.Clientset, namespace, name string) error {
|
|
||||||
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
|
||||||
// Create Deployment
|
|
||||||
deletePolicy := v1.DeletePropagationForeground
|
|
||||||
deleteOptions := v1.DeleteOptions{
|
|
||||||
PropagationPolicy: &deletePolicy,
|
|
||||||
GracePeriodSeconds: &gracePeriod,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
return deploymentsClient.Delete(name, &deleteOptions)
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := waitForDeploymentToDelete(clientset, namespace, name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Deleted deployment: %s\n", name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) createPreflightTestService(clientset *kubernetes.Clientset, namespace string, svcName string) (*apiv1.Service, error) {
|
|
||||||
iptr := int32Ptr(80)
|
|
||||||
servicesClient := clientset.CoreV1().Services(namespace)
|
|
||||||
service := &apiv1.Service{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: svcName,
|
|
||||||
Namespace: namespace,
|
|
||||||
Labels: map[string]string{
|
|
||||||
"app": "preflight-check",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Spec: apiv1.ServiceSpec{
|
|
||||||
Ports: []apiv1.ServicePort{
|
|
||||||
{Name: "port1",
|
|
||||||
Port: *iptr,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Selector: map[string]string{
|
|
||||||
"app": "preflight-check",
|
|
||||||
},
|
|
||||||
ClusterIP: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
var result *apiv1.Service
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
result, err = servicesClient.Create(service)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Created service %q\n", result.GetObjectMeta().GetName())
|
|
||||||
|
|
||||||
return service, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getService(clientset *kubernetes.Clientset, namespace, svcName string) (*apiv1.Service, error) {
|
|
||||||
servicesClient := clientset.CoreV1().Services(namespace)
|
|
||||||
var svc *apiv1.Service
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
svc, err = servicesClient.Get(svcName, v1.GetOptions{})
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
err = errors.Wrapf(err, "unable to get services in the %s namespace", namespace)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return svc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) deleteService(clientset *kubernetes.Clientset, namespace, name string) error {
|
|
||||||
servicesClient := clientset.CoreV1().Services(namespace)
|
|
||||||
// Create Deployment
|
|
||||||
deletePolicy := v1.DeletePropagationForeground
|
|
||||||
deleteOptions := v1.DeleteOptions{
|
|
||||||
PropagationPolicy: &deletePolicy,
|
|
||||||
}
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
return servicesClient.Delete(name, &deleteOptions)
|
|
||||||
}); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Deleted service: %s\n", name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) deletePod(clientset *kubernetes.Clientset, namespace, name string) error {
|
|
||||||
|
|
||||||
podsClient := clientset.CoreV1().Pods(namespace)
|
|
||||||
deletePolicy := v1.DeletePropagationForeground
|
|
||||||
deleteOptions := v1.DeleteOptions{
|
|
||||||
PropagationPolicy: &deletePolicy,
|
|
||||||
GracePeriodSeconds: &gracePeriod,
|
|
||||||
}
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
return podsClient.Delete(name, &deleteOptions)
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := waitForPodToDelete(clientset, namespace, name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Deleted pod: %s\n", name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) createPreflightTestPod(clientset *kubernetes.Clientset, namespace, podName, imageName string, secretNames []string, commandToRun []string) (*apiv1.Pod, error) {
|
|
||||||
// build the pod definition we want to deploy
|
|
||||||
pod := &apiv1.Pod{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: podName,
|
|
||||||
Namespace: namespace,
|
|
||||||
Labels: map[string]string{
|
|
||||||
"app": "preflight",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Spec: apiv1.PodSpec{
|
|
||||||
RestartPolicy: apiv1.RestartPolicyNever,
|
|
||||||
Containers: []apiv1.Container{
|
|
||||||
{
|
|
||||||
Name: "cnt",
|
|
||||||
Image: imageName,
|
|
||||||
ImagePullPolicy: apiv1.PullIfNotPresent,
|
|
||||||
Command: commandToRun,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if len(secretNames) > 0 {
|
|
||||||
for _, secretName := range secretNames {
|
|
||||||
pod.Spec.Volumes = append(pod.Spec.Volumes, apiv1.Volume{
|
|
||||||
Name: secretName,
|
|
||||||
VolumeSource: apiv1.VolumeSource{
|
|
||||||
Secret: &apiv1.SecretVolumeSource{
|
|
||||||
SecretName: secretName,
|
|
||||||
Items: []apiv1.KeyToPath{
|
|
||||||
{
|
|
||||||
Key: secretName,
|
|
||||||
Path: secretName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if len(pod.Spec.Containers) > 0 {
|
|
||||||
pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, apiv1.VolumeMount{
|
|
||||||
Name: secretName,
|
|
||||||
MountPath: "/etc/ssl/" + secretName,
|
|
||||||
ReadOnly: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now create the pod in kubernetes cluster using the clientset
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
pod, err = clientset.CoreV1().Pods(namespace).Create(pod)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Created pod: %s\n", pod.Name)
|
|
||||||
return pod, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPod(clientset *kubernetes.Clientset, namespace, podName string) (*apiv1.Pod, error) {
|
|
||||||
api.LogDebugMessage("Fetching pod: %s\n", podName)
|
|
||||||
var pod *apiv1.Pod
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
pod, err = clientset.CoreV1().Pods(namespace).Get(podName, v1.GetOptions{})
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
api.LogDebugMessage("%v\n", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pod, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPodLogs(clientset *kubernetes.Clientset, pod *apiv1.Pod) (string, error) {
|
|
||||||
podLogOpts := apiv1.PodLogOptions{}
|
|
||||||
|
|
||||||
api.LogDebugMessage("Retrieving logs for pod: %s namespace: %s\n", pod.GetName(), pod.Namespace)
|
|
||||||
req := clientset.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOpts)
|
|
||||||
podLogs, err := req.Stream()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer podLogs.Close()
|
|
||||||
time.Sleep(15 * time.Second)
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
_, err = io.Copy(buf, podLogs)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
api.LogDebugMessage("Log from pod: %s\n", buf.String())
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForResource(checkFunc func() (interface{}, error), validateFunc func(interface{}) bool) error {
|
|
||||||
timeout := time.NewTicker(2 * time.Minute)
|
|
||||||
defer timeout.Stop()
|
|
||||||
OUT:
|
|
||||||
for {
|
|
||||||
r, err := checkFunc()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-timeout.C:
|
|
||||||
break OUT
|
|
||||||
default:
|
|
||||||
if validateFunc(r) {
|
|
||||||
break OUT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDeployment *appsv1.Deployment) error {
|
|
||||||
var err error
|
|
||||||
depName := pfDeployment.GetName()
|
|
||||||
checkFunc := func() (interface{}, error) {
|
|
||||||
pfDeployment, err = getDeployment(clientset, namespace, depName)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("unable to retrieve deployment: %s\n", depName)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pfDeployment, nil
|
|
||||||
}
|
|
||||||
validateFunc := func(data interface{}) bool {
|
|
||||||
d := data.(*appsv1.Deployment)
|
|
||||||
return int(d.Status.ReadyReplicas) > 0
|
|
||||||
}
|
|
||||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if int(pfDeployment.Status.ReadyReplicas) == 0 {
|
|
||||||
err = fmt.Errorf("deployment took longer than expected to spin up pods")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error {
|
|
||||||
var err error
|
|
||||||
if len(pod.Spec.Containers) == 0 {
|
|
||||||
err = fmt.Errorf("there are no containers in the pod")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
podName := pod.Name
|
|
||||||
checkFunc := func() (interface{}, error) {
|
|
||||||
pod, err = getPod(clientset, namespace, podName)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("unable to retrieve %s pod by name", podName)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pod, nil
|
|
||||||
}
|
|
||||||
validateFunc := func(data interface{}) bool {
|
|
||||||
po := data.(*apiv1.Pod)
|
|
||||||
return len(po.Status.ContainerStatuses) > 0 && po.Status.ContainerStatuses[0].Ready
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(pod.Status.ContainerStatuses) == 0 || !pod.Status.ContainerStatuses[0].Ready {
|
|
||||||
err = fmt.Errorf("container is taking much longer than expected")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForPodToDie(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error {
|
|
||||||
podName := pod.Name
|
|
||||||
checkFunc := func() (interface{}, error) {
|
|
||||||
po, err := getPod(clientset, namespace, podName)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("unable to retrieve %s pod by name", podName)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
api.LogDebugMessage("pod status: %v\n", po.Status.Phase)
|
|
||||||
return po, nil
|
|
||||||
}
|
|
||||||
validateFunc := func(r interface{}) bool {
|
|
||||||
po := r.(*apiv1.Pod)
|
|
||||||
return po.Status.Phase == apiv1.PodFailed || po.Status.Phase == apiv1.PodSucceeded
|
|
||||||
}
|
|
||||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForPodToDelete(clientset *kubernetes.Clientset, namespace, podName string) error {
|
|
||||||
checkFunc := func() (interface{}, error) {
|
|
||||||
po, err := getPod(clientset, namespace, podName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return po, nil
|
|
||||||
}
|
|
||||||
validateFunc := func(po interface{}) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := fmt.Errorf("delete pod is taking unusually long")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForDeploymentToDelete(clientset *kubernetes.Clientset, namespace, deploymentName string) error {
|
|
||||||
checkFunc := func() (interface{}, error) {
|
|
||||||
dep, err := getDeployment(clientset, namespace, deploymentName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return dep, nil
|
|
||||||
}
|
|
||||||
validateFunc := func(po interface{}) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := fmt.Errorf("delete deployment is taking unusually long")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) createPfRole(clientset *kubernetes.Clientset, namespace, roleName string) (*v1beta1.Role, error) {
|
|
||||||
// build the role defination we want to create
|
|
||||||
var role *v1beta1.Role
|
|
||||||
roleSpec := &v1beta1.Role{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: roleName,
|
|
||||||
Namespace: namespace,
|
|
||||||
Labels: map[string]string{
|
|
||||||
"app": "preflight",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Rules: []v1beta1.PolicyRule{},
|
|
||||||
}
|
|
||||||
|
|
||||||
// now create the role in kubernetes cluster using the clientset
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
role, err = clientset.RbacV1beta1().Roles(namespace).Create(roleSpec)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
qp.P.LogVerboseMessage("Created role: %s\n", role.Name)
|
|
||||||
|
|
||||||
return role, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) deleteRole(clientset *kubernetes.Clientset, namespace string, role *v1beta1.Role) {
|
|
||||||
rolesClient := clientset.RbacV1beta1().Roles(namespace)
|
|
||||||
|
|
||||||
deletePolicy := v1.DeletePropagationForeground
|
|
||||||
deleteOptions := v1.DeleteOptions{
|
|
||||||
PropagationPolicy: &deletePolicy,
|
|
||||||
}
|
|
||||||
err := rolesClient.Delete(role.GetName(), &deleteOptions)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Deleted role: %s\n\n", role.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) createPfRoleBinding(clientset *kubernetes.Clientset, namespace, roleBindingName string) (*v1beta1.RoleBinding, error) {
|
|
||||||
var roleBinding *v1beta1.RoleBinding
|
|
||||||
// build the rolebinding defination we want to create
|
|
||||||
roleBindingSpec := &v1beta1.RoleBinding{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: roleBindingName,
|
|
||||||
Namespace: namespace,
|
|
||||||
Labels: map[string]string{
|
|
||||||
"app": "preflight",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Subjects: []v1beta1.Subject{
|
|
||||||
{
|
|
||||||
Kind: "ServiceAccount",
|
|
||||||
APIGroup: "",
|
|
||||||
Name: "preflight-check-subject",
|
|
||||||
Namespace: namespace,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RoleRef: v1beta1.RoleRef{
|
|
||||||
APIGroup: "",
|
|
||||||
Kind: "Role",
|
|
||||||
Name: "preflight-check-roleref",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// now create the roleBinding in kubernetes cluster using the clientset
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
roleBinding, err = clientset.RbacV1beta1().RoleBindings(namespace).Create(roleBindingSpec)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Created RoleBinding: %s\n", roleBindingSpec.Name)
|
|
||||||
return roleBinding, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) deleteRoleBinding(clientset *kubernetes.Clientset, namespace string, roleBinding *v1beta1.RoleBinding) {
|
|
||||||
roleBindingClient := clientset.RbacV1beta1().RoleBindings(namespace)
|
|
||||||
|
|
||||||
deletePolicy := v1.DeletePropagationForeground
|
|
||||||
deleteOptions := v1.DeleteOptions{
|
|
||||||
PropagationPolicy: &deletePolicy,
|
|
||||||
}
|
|
||||||
err := roleBindingClient.Delete(roleBinding.GetName(), &deleteOptions)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Deleted RoleBinding: %s\n\n", roleBinding.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) createPfServiceAccount(clientset *kubernetes.Clientset, namespace, serviceAccountName string) (*apiv1.ServiceAccount, error) {
|
|
||||||
var serviceAccount *apiv1.ServiceAccount
|
|
||||||
// build the serviceAccount defination we want to create
|
|
||||||
serviceAccountSpec := &apiv1.ServiceAccount{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: "preflight-check-test-serviceaccount",
|
|
||||||
Namespace: namespace,
|
|
||||||
Labels: map[string]string{
|
|
||||||
"app": "preflight",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// now create the serviceAccount in kubernetes cluster using the clientset
|
|
||||||
if err := retryOnError(func() (err error) {
|
|
||||||
serviceAccount, err = clientset.CoreV1().ServiceAccounts(namespace).Create(serviceAccountSpec)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Created Service Account: %s\n", serviceAccountSpec.Name)
|
|
||||||
return serviceAccount, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) deleteServiceAccount(clientset *kubernetes.Clientset, namespace string, serviceAccount *apiv1.ServiceAccount) {
|
|
||||||
serviceAccountClient := clientset.CoreV1().ServiceAccounts(namespace)
|
|
||||||
|
|
||||||
deletePolicy := v1.DeletePropagationForeground
|
|
||||||
deleteOptions := v1.DeleteOptions{
|
|
||||||
PropagationPolicy: &deletePolicy,
|
|
||||||
}
|
|
||||||
err := serviceAccountClient.Delete(serviceAccount.GetName(), &deleteOptions)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Deleted ServiceAccount: %s\n\n", serviceAccount.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) createPreflightTestSecret(clientset *kubernetes.Clientset, namespace, secretName string, secretData []byte) (*apiv1.Secret, error) {
|
|
||||||
var secret *apiv1.Secret
|
|
||||||
var err error
|
|
||||||
// build the secret defination we want to create
|
|
||||||
secretSpec := &apiv1.Secret{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Name: secretName,
|
|
||||||
Namespace: namespace,
|
|
||||||
Labels: map[string]string{
|
|
||||||
"app": "preflight",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Data: map[string][]byte{
|
|
||||||
secretName: secretData,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// now create the secret in kubernetes cluster using the clientset
|
|
||||||
if err = retryOnError(func() (err error) {
|
|
||||||
secret, err = clientset.CoreV1().Secrets(namespace).Create(secretSpec)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Created Secret: %s\n", secret.Name)
|
|
||||||
return secret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) deleteK8sSecret(clientset *kubernetes.Clientset, namespace string, k8sSecret *apiv1.Secret) {
|
|
||||||
secretClient := clientset.CoreV1().Secrets(namespace)
|
|
||||||
|
|
||||||
deletePolicy := v1.DeletePropagationForeground
|
|
||||||
deleteOptions := v1.DeleteOptions{
|
|
||||||
PropagationPolicy: &deletePolicy,
|
|
||||||
}
|
|
||||||
err := secretClient.Delete(k8sSecret.GetName(), &deleteOptions)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
qp.P.LogVerboseMessage("Deleted Secret: %s\n", k8sSecret.Name)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
package preflight
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_initiateK8sOps(t *testing.T) {
|
|
||||||
t.Skip()
|
|
||||||
type args struct {
|
|
||||||
opr string
|
|
||||||
namespace string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid case",
|
|
||||||
args: args{
|
|
||||||
opr: fmt.Sprintf("version"),
|
|
||||||
namespace: "test-ns",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid case",
|
|
||||||
args: args{
|
|
||||||
opr: fmt.Sprintf("versions"),
|
|
||||||
namespace: "test-ns",
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if err := initiateK8sOps(tt.args.opr, tt.args.namespace); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("initiateK8sOps() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,58 +11,69 @@ import (
|
|||||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
)
|
)
|
||||||
|
|
||||||
var resultYamlBytes = []byte("")
|
func (qp *QliksensePreflight) CheckCreateRole(namespace string, cleanup bool) error {
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckCreateRole(namespace string) error {
|
|
||||||
// create a Role
|
// create a Role
|
||||||
qp.P.LogVerboseMessage("Preflight role check: \n")
|
if !cleanup {
|
||||||
qp.P.LogVerboseMessage("--------------------- \n")
|
fmt.Print("Preflight role check... ")
|
||||||
err := qp.checkCreateEntity(namespace, "Role")
|
qp.CG.LogVerboseMessage("\n--------------------- \n")
|
||||||
|
}
|
||||||
|
err := qp.checkCreateEntity(namespace, "Role", cleanup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Completed preflight role check\n")
|
if !cleanup {
|
||||||
|
qp.CG.LogVerboseMessage("Completed preflight role check\n")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckCreateRoleBinding(namespace string) error {
|
func (qp *QliksensePreflight) CheckCreateRoleBinding(namespace string, cleanup bool) error {
|
||||||
// create a RoleBinding
|
// create a RoleBinding
|
||||||
qp.P.LogVerboseMessage("Preflight rolebinding check: \n")
|
if !cleanup {
|
||||||
qp.P.LogVerboseMessage("---------------------------- \n")
|
fmt.Print("Preflight rolebinding check... ")
|
||||||
err := qp.checkCreateEntity(namespace, "RoleBinding")
|
qp.CG.LogVerboseMessage("\n---------------------------- \n")
|
||||||
|
}
|
||||||
|
err := qp.checkCreateEntity(namespace, "RoleBinding", cleanup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Completed preflight rolebinding check\n")
|
if !cleanup {
|
||||||
|
qp.CG.LogVerboseMessage("Completed preflight rolebinding check\n")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckCreateServiceAccount(namespace string) error {
|
func (qp *QliksensePreflight) CheckCreateServiceAccount(namespace string, cleanup bool) error {
|
||||||
// create a service account
|
// create a service account
|
||||||
qp.P.LogVerboseMessage("Preflight serviceaccount check: \n")
|
if !cleanup {
|
||||||
qp.P.LogVerboseMessage("------------------------------- \n")
|
fmt.Print("Preflight serviceaccount check... ")
|
||||||
err := qp.checkCreateEntity(namespace, "ServiceAccount")
|
qp.CG.LogVerboseMessage("\n------------------------------- \n")
|
||||||
|
}
|
||||||
|
err := qp.checkCreateEntity(namespace, "ServiceAccount", cleanup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Completed preflight serviceaccount check\n")
|
if !cleanup {
|
||||||
|
qp.CG.LogVerboseMessage("Completed preflight serviceaccount check\n")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string) error {
|
func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string, cleanup bool) error {
|
||||||
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
|
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
|
||||||
var currentCR *qapi.QliksenseCR
|
var currentCR *qapi.QliksenseCR
|
||||||
mfroot := ""
|
mfroot := ""
|
||||||
kusDir := ""
|
kusDir := ""
|
||||||
|
resultYamlBytes := []byte("")
|
||||||
var err error
|
var err error
|
||||||
currentCR, err = qConfig.GetCurrentCR()
|
currentCR, err = qConfig.GetCurrentCR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
qp.P.LogVerboseMessage("Unable to retrieve current CR: %v\n", err)
|
qp.CG.LogVerboseMessage("Unable to retrieve current CR: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if currentCR.IsRepoExist() {
|
if currentCR.IsRepoExist() {
|
||||||
mfroot = currentCR.Spec.GetManifestsRoot()
|
mfroot = currentCR.Spec.GetManifestsRoot()
|
||||||
} else if tempDownloadedDir, err := qliksense.DownloadFromGitRepoToTmpDir(qliksense.QLIK_GIT_REPO, "master"); err != nil {
|
} else if tempDownloadedDir, err := qliksense.DownloadFromGitRepoToTmpDir(qliksense.QLIK_GIT_REPO, "master"); err != nil {
|
||||||
qp.P.LogVerboseMessage("Unable to Download from git repo to tmp dir: %v\n", err)
|
qp.CG.LogVerboseMessage("Unable to Download from git repo to tmp dir: %v\n", err)
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
mfroot = tempDownloadedDir
|
mfroot = tempDownloadedDir
|
||||||
@@ -84,16 +95,23 @@ func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string)
|
|||||||
if sa != "" {
|
if sa != "" {
|
||||||
sa = strings.Replace(sa, "name: qliksense", "name: preflight", -1)
|
sa = strings.Replace(sa, "name: qliksense", "name: preflight", -1)
|
||||||
} else {
|
} else {
|
||||||
err := fmt.Errorf("Unable to retrieve yamls to apply on cluster from dir: %s, error: %v", kusDir, err)
|
err = fmt.Errorf(`We were unable to retrieve valid %ss from running "kustomize" in your %s directory.
|
||||||
|
Please check the value in the "Profile" field of your CR. `, strings.ToLower(entityToTest), kusDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
namespace = "" // namespace is handled when generating the manifests
|
namespace = "" // namespace is handled when generating the manifests
|
||||||
|
|
||||||
|
// check if entity already exists in the cluster, if so - delete it
|
||||||
|
api.KubectlDeleteVerbose(sa, namespace, qp.P.Verbose)
|
||||||
|
if cleanup {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
qp.P.LogVerboseMessage("Cleaning up resources...\n")
|
qp.CG.LogVerboseMessage("Cleaning up resources...\n")
|
||||||
err := api.KubectlDeleteVerbose(sa, namespace, qp.P.Verbose)
|
err := api.KubectlDeleteVerbose(sa, namespace, qp.P.Verbose)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
qp.P.LogVerboseMessage("Preflight cleanup failed!\n")
|
qp.CG.LogVerboseMessage("Preflight cleanup failed!\n")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -103,55 +121,55 @@ func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
qp.P.LogVerboseMessage("Preflight %s check: PASSED\n", entityToTest)
|
qp.CG.LogVerboseMessage("Preflight %s check: PASSED\n", entityToTest)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckCreateRB(namespace string, kubeConfigContents []byte) error {
|
func (qp *QliksensePreflight) CheckCreateRB(namespace string, kubeConfigContents []byte) error {
|
||||||
|
|
||||||
// create a role
|
// create a role
|
||||||
qp.P.LogVerboseMessage("Preflight createRole check: \n")
|
qp.CG.LogVerboseMessage("Preflight createRole check: \n")
|
||||||
qp.P.LogVerboseMessage("--------------------------- \n")
|
qp.CG.LogVerboseMessage("--------------------------- \n")
|
||||||
errStr := strings.Builder{}
|
errStr := strings.Builder{}
|
||||||
err1 := qp.checkCreateEntity(namespace, "Role")
|
err1 := qp.checkCreateEntity(namespace, "Role", false)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
errStr.WriteString(err1.Error())
|
errStr.WriteString(err1.Error())
|
||||||
errStr.WriteString("\n")
|
errStr.WriteString("\n")
|
||||||
qp.P.LogVerboseMessage("%v\n", err1)
|
qp.CG.LogVerboseMessage("%v\n", err1)
|
||||||
qp.P.LogVerboseMessage("Preflight role check: FAILED\n")
|
qp.CG.LogVerboseMessage("Preflight role check: FAILED\n")
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Completed preflight role check\n\n")
|
qp.CG.LogVerboseMessage("Completed preflight role check\n\n")
|
||||||
|
|
||||||
// create a roleBinding
|
// create a roleBinding
|
||||||
qp.P.LogVerboseMessage("Preflight rolebinding check: \n")
|
qp.CG.LogVerboseMessage("Preflight rolebinding check: \n")
|
||||||
qp.P.LogVerboseMessage("---------------------------- \n")
|
qp.CG.LogVerboseMessage("---------------------------- \n")
|
||||||
err2 := qp.checkCreateEntity(namespace, "RoleBinding")
|
err2 := qp.checkCreateEntity(namespace, "RoleBinding", false)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
errStr.WriteString(err2.Error())
|
errStr.WriteString(err2.Error())
|
||||||
errStr.WriteString("\n")
|
errStr.WriteString("\n")
|
||||||
qp.P.LogVerboseMessage("%v\n", err2)
|
qp.CG.LogVerboseMessage("%v\n", err2)
|
||||||
qp.P.LogVerboseMessage("Preflight rolebinding check: FAILED\n")
|
qp.CG.LogVerboseMessage("Preflight rolebinding check: FAILED\n")
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Completed preflight rolebinding check\n\n")
|
qp.CG.LogVerboseMessage("Completed preflight rolebinding check\n\n")
|
||||||
|
|
||||||
// create a service account
|
// create a service account
|
||||||
qp.P.LogVerboseMessage("Preflight serviceaccount check: \n")
|
qp.CG.LogVerboseMessage("Preflight serviceaccount check: \n")
|
||||||
qp.P.LogVerboseMessage("------------------------------- \n")
|
qp.CG.LogVerboseMessage("------------------------------- \n")
|
||||||
err3 := qp.checkCreateEntity(namespace, "ServiceAccount")
|
err3 := qp.checkCreateEntity(namespace, "ServiceAccount", false)
|
||||||
if err3 != nil {
|
if err3 != nil {
|
||||||
errStr.WriteString(err3.Error())
|
errStr.WriteString(err3.Error())
|
||||||
errStr.WriteString("\n")
|
errStr.WriteString("\n")
|
||||||
qp.P.LogVerboseMessage("%v\n", err3)
|
qp.CG.LogVerboseMessage("%v\n", err3)
|
||||||
qp.P.LogVerboseMessage("Preflight serviceaccount check: FAILED\n")
|
qp.CG.LogVerboseMessage("Preflight serviceaccount check: FAILED\n")
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Completed preflight serviceaccount check\n\n")
|
qp.CG.LogVerboseMessage("Completed preflight serviceaccount check\n\n")
|
||||||
|
|
||||||
if err1 != nil || err2 != nil || err3 != nil {
|
if err1 != nil || err2 != nil || err3 != nil {
|
||||||
qp.P.LogVerboseMessage("Preflight authcheck: FAILED\n")
|
qp.CG.LogVerboseMessage("Preflight authcheck: FAILED\n")
|
||||||
qp.P.LogVerboseMessage("Completed preflight authcheck\n")
|
qp.CG.LogVerboseMessage("Completed preflight authcheck\n")
|
||||||
return errors.New(errStr.String())
|
return errors.New(errStr.String())
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Preflight authcheck: PASSED\n")
|
qp.CG.LogVerboseMessage("Preflight authcheck: PASSED\n")
|
||||||
qp.P.LogVerboseMessage("Completed preflight authcheck\n")
|
qp.CG.LogVerboseMessage("Completed preflight authcheck\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
123
pkg/preflight/verify_ca.go
Normal file
123
pkg/preflight/verify_ca.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package preflight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) VerifyCAChain(kubeConfigContents []byte, namespace string, preflightOpts *PreflightOptions, cleanup bool) error {
|
||||||
|
|
||||||
|
var currentCR *qapi.QliksenseCR
|
||||||
|
var err error
|
||||||
|
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
|
||||||
|
qConfig.SetNamespace(namespace)
|
||||||
|
|
||||||
|
fmt.Print("Preflight verify-ca-chain check... ")
|
||||||
|
qp.CG.LogVerboseMessage("\n----------------------------------- \n")
|
||||||
|
|
||||||
|
currentCR, err = qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
qp.CG.LogVerboseMessage("Unable to retrieve current CR: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decryptedCR, err := qConfig.GetDecryptedCr(currentCR)
|
||||||
|
if err != nil {
|
||||||
|
qp.CG.LogVerboseMessage("An error occurred while retrieving mongodbUrl from current CR: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// infer ca certs form CR
|
||||||
|
caCertificates := strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("qliksense", "caCertificates"))
|
||||||
|
|
||||||
|
fmt.Println("Openssl verify mongodbUrl:")
|
||||||
|
// infer mongodb url from CR
|
||||||
|
mongodbUrl := strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("qliksense", "mongodbUri"))
|
||||||
|
qp.CG.LogVerboseMessage("Mongodb url inferred form CR: %s\n", mongodbUrl)
|
||||||
|
|
||||||
|
// parse out server and port from mongodb url and execute openssl verify
|
||||||
|
if err := qp.extractCertAndVerify(mongodbUrl, caCertificates); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\nOpenssl verify discoveryUrl:\n")
|
||||||
|
// infer idpConfigs form CR
|
||||||
|
idpConfigs := strings.TrimSpace(decryptedCR.Spec.GetFromSecrets("identity-providers", "idpConfigs"))
|
||||||
|
|
||||||
|
data := []map[string]interface{}{}
|
||||||
|
if err := json.Unmarshal([]byte(idpConfigs), &data); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var discoveryUrl string
|
||||||
|
for _, idpData := range data {
|
||||||
|
discoveryUrl = idpData["discoveryUrl"].(string)
|
||||||
|
qp.CG.LogVerboseMessage("Discovery url: %s\n", discoveryUrl)
|
||||||
|
}
|
||||||
|
if err := qp.extractCertAndVerify(discoveryUrl, caCertificates); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
qp.CG.LogVerboseMessage("Completed preflight verify-ca-chain check\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) extractCertAndVerify(server string, caCertificates string) error {
|
||||||
|
u, err := url.Parse(server)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to parse url: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.ToLower(u.Scheme) {
|
||||||
|
case "http":
|
||||||
|
return fmt.Errorf("http url is not supported for this operation")
|
||||||
|
case "https":
|
||||||
|
if u.Port() == "" {
|
||||||
|
u.Host += ":443"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qp.CG.LogVerboseMessage("Host: %s, port: %s\n", u.Host, u.Port())
|
||||||
|
conn, err := tls.Dial("tcp", u.Host, &tls.Config{})
|
||||||
|
qp.CG.LogVerboseMessage("Host: %s\n", u.Host)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to connect: " + err.Error())
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Get the ConnectionState struct as that's the one which gives us x509.Certificate struct
|
||||||
|
x509Certificates := conn.ConnectionState().PeerCertificates
|
||||||
|
|
||||||
|
var serverCert *x509.Certificate
|
||||||
|
if len(x509Certificates) == 0 {
|
||||||
|
return fmt.Errorf("no server certificates retrieved from the server")
|
||||||
|
}
|
||||||
|
// we retrieve and verify the server certificate, we ignore intermediate certificates at this point.
|
||||||
|
for _, x509Cert := range x509Certificates {
|
||||||
|
if !x509Cert.IsCA {
|
||||||
|
serverCert = x509Cert
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if serverCert == nil {
|
||||||
|
return fmt.Errorf("no valid server certificates retrieved from the server")
|
||||||
|
}
|
||||||
|
roots := x509.NewCertPool()
|
||||||
|
if ok := roots.AppendCertsFromPEM([]byte(caCertificates)); !ok {
|
||||||
|
return fmt.Errorf("failed to parse root certificate.")
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := x509.VerifyOptions{
|
||||||
|
Roots: roots,
|
||||||
|
DNSName: u.Hostname(),
|
||||||
|
}
|
||||||
|
if _, err := serverCert.Verify(opts); err != nil {
|
||||||
|
return fmt.Errorf("failed to verify certificate: " + err.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -8,25 +8,25 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/version"
|
"k8s.io/apimachinery/pkg/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigContents []byte) error {
|
func (p *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigContents []byte) error {
|
||||||
qp.P.LogVerboseMessage("Preflight kubernetes version check: \n")
|
fmt.Print("Preflight kubernetes version check... ")
|
||||||
qp.P.LogVerboseMessage("----------------------------------- \n")
|
p.CG.LogVerboseMessage("\n----------------------------------- \n")
|
||||||
var currentVersion *semver.Version
|
var currentVersion *semver.Version
|
||||||
|
|
||||||
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
clientset, _, err := p.CG.GetK8SClientSet(kubeConfigContents, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Unable to create clientset: %v\n", err)
|
err = fmt.Errorf("Unable to create clientset: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var serverVersion *version.Info
|
var serverVersion *version.Info
|
||||||
if err := retryOnError(func() (err error) {
|
if err := p.CG.RetryOnError(func() (err error) {
|
||||||
serverVersion, err = clientset.ServerVersion()
|
serverVersion, err = clientset.ServerVersion()
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
err = fmt.Errorf("Unable to get server version: %v\n", err)
|
err = fmt.Errorf("Unable to get server version: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qp.P.LogVerboseMessage("Kubernetes API Server version: %s\n", serverVersion.String())
|
p.CG.LogVerboseMessage("Kubernetes API Server version: %s\n", serverVersion.String())
|
||||||
|
|
||||||
// Compare K8s version on the cluster with minimum supported k8s version
|
// Compare K8s version on the cluster with minimum supported k8s version
|
||||||
currentVersion, err = semver.NewVersion(serverVersion.String())
|
currentVersion, err = semver.NewVersion(serverVersion.String())
|
||||||
@@ -36,14 +36,14 @@ func (qp *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigConten
|
|||||||
}
|
}
|
||||||
api.LogDebugMessage("Current Kubernetes Version: %v\n", currentVersion)
|
api.LogDebugMessage("Current Kubernetes Version: %v\n", currentVersion)
|
||||||
|
|
||||||
minK8sVersionSemver, err := semver.NewVersion(qp.GetPreflightConfigObj().GetMinK8sVersion())
|
minK8sVersionSemver, err := semver.NewVersion(p.GetPreflightConfigObj().GetMinK8sVersion())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Unable to convert minimum Kubernetes version into semver version:%v\n", err)
|
err = fmt.Errorf("Unable to convert minimum Kubernetes version into semver version:%v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentVersion.GreaterThan(minK8sVersionSemver) {
|
if currentVersion.GreaterThan(minK8sVersionSemver) {
|
||||||
qp.P.LogVerboseMessage("Current Kubernetes API Server version %s is greater than or equal to minimum required version: %s\n", currentVersion, minK8sVersionSemver)
|
p.CG.LogVerboseMessage("Current Kubernetes API Server version %s is greater than or equal to minimum required version: %s\n", currentVersion, minK8sVersionSemver)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("Current Kubernetes API Server version %s is less than minimum required version: %s", currentVersion, minK8sVersionSemver)
|
err = fmt.Errorf("Current Kubernetes API Server version %s is less than minimum required version: %s", currentVersion, minK8sVersionSemver)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -23,13 +23,12 @@ type patch struct {
|
|||||||
Patch string `yaml:"patch"`
|
Patch string `yaml:"patch"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type selectivePatch struct {
|
type annotationTransformer struct {
|
||||||
APIVersion string `yaml:"apiVersion"`
|
APIVersion string `yaml:"apiVersion"`
|
||||||
Metadata struct {
|
Metadata struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
} `yaml:"metadata"`
|
} `yaml:"metadata"`
|
||||||
Enabled bool `yaml:"enabled"`
|
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||||
Patches []patch `yaml:"patches"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type helmChart struct {
|
type helmChart struct {
|
||||||
@@ -73,7 +72,7 @@ func (q *Qliksense) About(gitRef, profile string) (*VersionOutput, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (q *Qliksense) AboutDir(configDirectory, profile string) (*VersionOutput, error) {
|
func (q *Qliksense) AboutDir(configDirectory, profile string) (*VersionOutput, error) {
|
||||||
if chartVersion, err := getChartVersion(filepath.Join(configDirectory, "transformers", "qseokversion.yaml"), "qliksense"); err != nil {
|
if chartVersion, err := getChartVersion(filepath.Join(configDirectory, "manifests", "base", "transformers", "release", "annotations.yaml"), "app.kubernetes.io/version"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if kuzManifest, err := executeKustomizeBuildWithStdoutProgress(filepath.Join(configDirectory, "manifests", profile)); err != nil {
|
} else if kuzManifest, err := executeKustomizeBuildWithStdoutProgress(filepath.Join(configDirectory, "manifests", profile)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -223,22 +222,16 @@ func traverseYamlDecodedMapRecursively(val reflect.Value, path []string, visitor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChartVersion(versionFile, chartName string) (string, error) {
|
func getChartVersion(versionFile, versionAnnotation string) (string, error) {
|
||||||
var patchInst patch
|
var annTransformer annotationTransformer
|
||||||
var selPatch selectivePatch
|
|
||||||
var chart helmChart
|
|
||||||
|
|
||||||
if bytes, err := ioutil.ReadFile(versionFile); err != nil {
|
if bytes, err := ioutil.ReadFile(versionFile); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
} else if err = yaml.Unmarshal(bytes, &selPatch); err != nil {
|
} else if err = yaml.Unmarshal(bytes, &annTransformer); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
for _, patchInst = range selPatch.Patches {
|
if version, ok := annTransformer.Annotations[versionAnnotation]; ok {
|
||||||
if err := yaml.Unmarshal([]byte(patchInst.Patch), &chart); err == nil {
|
return version, nil
|
||||||
if chart.ChartName == chartName {
|
|
||||||
return chart.ChartVersion, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +1,8 @@
|
|||||||
package qliksense
|
package qliksense
|
||||||
|
|
||||||
import (
|
func (q *Qliksense) ApplyCRFromBytes(crBytes []byte, opts *InstallCommandOptions, overwriteExistingContext bool) error {
|
||||||
"fmt"
|
if err := q.LoadCr(crBytes, overwriteExistingContext); err != nil {
|
||||||
"io"
|
|
||||||
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (q *Qliksense) ApplyCRFromReader(r io.Reader, opts *InstallCommandOptions, keepPatchFiles, overwriteExistingContext, pull, push bool) error {
|
|
||||||
if err := q.LoadCr(r, overwriteExistingContext); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
return q.InstallQK8s("", opts)
|
||||||
cr, err := qConfig.GetCurrentCR()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
version := cr.GetLabelFromCr("version")
|
|
||||||
|
|
||||||
if pull {
|
|
||||||
fmt.Println("Pulling images...")
|
|
||||||
if err := q.PullImages(version, ""); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if push {
|
|
||||||
fmt.Println("Pushing images...")
|
|
||||||
if err := q.PushImagesForCurrentCR(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsQliksenseInstalled(cr.GetName()) {
|
|
||||||
// it is needed in case want to upgrade from one version to another
|
|
||||||
if cr.Spec.ManifestsRoot == "" && cr.Spec.Git == nil {
|
|
||||||
if !qConfig.IsRepoExistForCurrent(version) {
|
|
||||||
if err := q.FetchQK8s(version); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return q.UpgradeQK8s(keepPatchFiles)
|
|
||||||
}
|
|
||||||
return q.InstallQK8s(version, opts, keepPatchFiles)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsQliksenseInstalled(crName string) bool {
|
|
||||||
args := []string{
|
|
||||||
"get",
|
|
||||||
"qliksense",
|
|
||||||
crName,
|
|
||||||
"-ogo-template",
|
|
||||||
`--template='{{ .metadata.name}}'`,
|
|
||||||
}
|
|
||||||
_, err := qapi.KubectlDirectOps(args, "")
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,19 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qlik-oss/k-apis/pkg/config"
|
||||||
|
|
||||||
"github.com/mitchellh/go-homedir"
|
"github.com/mitchellh/go-homedir"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"github.com/qlik-oss/k-apis/pkg/cr"
|
"github.com/qlik-oss/k-apis/pkg/cr"
|
||||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
"k8s.io/kubectl/pkg/cmd/util/editor"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -39,9 +42,9 @@ func (q *Qliksense) ConfigApplyQK8s() error {
|
|||||||
return errors.New(agreementTempalte + "\nPlease do $ qliksense config set-configs qliksense.acceptEULA=yes\n")
|
return errors.New(agreementTempalte + "\nPlease do $ qliksense config set-configs qliksense.acceptEULA=yes\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// create patch dependent resoruces
|
// create patch dependent resources
|
||||||
fmt.Println("Installing resoruces used kuztomize patch")
|
fmt.Println("Installing resources used by the kuztomize patch")
|
||||||
if err := q.createK8sResoruceBeforePatch(qcr); err != nil {
|
if err := q.createK8sResourceBeforePatch(qcr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,27 +76,30 @@ func (q *Qliksense) configEjson() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (q *Qliksense) applyConfigToK8s(qcr *qapi.QliksenseCR) error {
|
func (q *Qliksense) applyConfigToK8s(qcr *qapi.QliksenseCR) error {
|
||||||
if qcr.Spec.RotateKeys != "None" {
|
if err := q.configEjson(); err != nil {
|
||||||
if err := q.configEjson(); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userHomeDir, err := homedir.Dir()
|
userHomeDir, err := homedir.Dir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf(`error fetching user's home directory: %v\n`, err)
|
fmt.Printf(`error fetching user's home directory: %v\n`, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("Manifests root: " + qcr.Spec.GetManifestsRoot())
|
|
||||||
qcr.SetNamespace(qapi.GetKubectlNamespace())
|
qcr.SetNamespace(qapi.GetKubectlNamespace())
|
||||||
|
b, _ := yaml.Marshal(qcr.KApiCr)
|
||||||
|
fmt.Printf("%v", string(b))
|
||||||
|
// os.Exit(0)
|
||||||
// generate patches
|
// generate patches
|
||||||
cr.GeneratePatches(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config"))
|
cr.GeneratePatches(&qcr.KApiCr, config.KeysActionRestoreOrRotate, path.Join(userHomeDir, ".kube", "config"))
|
||||||
// apply generated manifests
|
// apply generated manifests
|
||||||
profilePath := filepath.Join(qcr.Spec.GetManifestsRoot(), qcr.Spec.GetProfileDir())
|
profilePath := filepath.Join(qcr.Spec.GetManifestsRoot(), qcr.Spec.GetProfileDir())
|
||||||
|
fmt.Printf("Generating manifests for profile: %v\n", profilePath)
|
||||||
mByte, err := ExecuteKustomizeBuild(profilePath)
|
mByte, err := ExecuteKustomizeBuild(profilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("cannot generate manifests for "+profilePath, err)
|
fmt.Printf("error generating manifests: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fmt.Println("Applying manifests to the cluster")
|
||||||
if err = qapi.KubectlApply(string(mByte), qcr.GetNamespace()); err != nil {
|
if err = qapi.KubectlApply(string(mByte), qcr.GetNamespace()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -190,13 +196,12 @@ func (q *Qliksense) EditCR(contextName string) error {
|
|||||||
if err := ioutil.WriteFile(tempFile.Name(), crContent, os.ModePerm); err != nil {
|
if err := ioutil.WriteFile(tempFile.Name(), crContent, os.ModePerm); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cmd := exec.Command(getKubeEditorTool(), tempFile.Name())
|
|
||||||
cmd.Stdin = os.Stdin
|
currentEditor := editor.NewDefaultEditor([]string{"KUBE_EDITOR", "EDITOR"})
|
||||||
cmd.Stdout = os.Stdout
|
if err = currentEditor.Launch(tempFile.Name()); err != nil {
|
||||||
err = cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
newCr, err := qapi.GetCRObject(tempFile.Name())
|
newCr, err := qapi.GetCRObject(tempFile.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("cannot save the cr. Someting wrong in the file format. It is not saved\n" + err.Error())
|
return errors.New("cannot save the cr. Someting wrong in the file format. It is not saved\n" + err.Error())
|
||||||
@@ -211,11 +216,3 @@ func (q *Qliksense) EditCR(contextName string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getKubeEditorTool() string {
|
|
||||||
editor := os.Getenv("KUBE_EDITOR")
|
|
||||||
if editor == "" {
|
|
||||||
editor = "vim"
|
|
||||||
}
|
|
||||||
return editor
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ import (
|
|||||||
|
|
||||||
b64 "encoding/base64"
|
b64 "encoding/base64"
|
||||||
|
|
||||||
|
. "github.com/logrusorgru/aurora"
|
||||||
ansi "github.com/mattn/go-colorable"
|
ansi "github.com/mattn/go-colorable"
|
||||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
_ "gopkg.in/yaml.v2"
|
_ "gopkg.in/yaml.v2"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
. "github.com/logrusorgru/aurora"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -62,7 +62,7 @@ func (q *Qliksense) SetSecrets(args []string, isSecretSet bool, base64Encoded bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Metadata name in qliksense CR is the name of the current context
|
// Metadata name in qliksense CR is the name of the current context
|
||||||
api.LogDebugMessage("Current context: %s", qliksenseCR.GetName())
|
api.LogDebugMessage("Current context: %s\n", qliksenseCR.GetName())
|
||||||
encryptionKey, err := qConfig.GetEncryptionKeyForCurrent()
|
encryptionKey, err := qConfig.GetEncryptionKeyForCurrent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -72,7 +72,7 @@ func (q *Qliksense) SetSecrets(args []string, isSecretSet bool, base64Encoded bo
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, ra := range resultArgs {
|
for _, ra := range resultArgs {
|
||||||
api.LogDebugMessage("value args to be encrypted: %s", ra.Value)
|
api.LogDebugMessage("value args to be encrypted: %s\n", ra.Value)
|
||||||
if err := q.processSecret(ra, encryptionKey, qliksenseCR, isSecretSet); err != nil {
|
if err := q.processSecret(ra, encryptionKey, qliksenseCR, isSecretSet); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -176,53 +176,6 @@ func caseInsenstiveFieldByName(v reflect.Value, name string) reflect.Value {
|
|||||||
return v.FieldByNameFunc(func(n string) bool { return strings.ToLower(n) == name })
|
return v.FieldByNameFunc(func(n string) bool { return strings.ToLower(n) == name })
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateCR(key string, keySub string, value string, crSpec *api.QliksenseCR) (bool, *api.QliksenseCR) {
|
|
||||||
cr := reflect.ValueOf(crSpec.Spec)
|
|
||||||
keyValid := caseInsenstiveFieldByName(reflect.Indirect(cr), key)
|
|
||||||
if !keyValid.IsValid() {
|
|
||||||
//not in main spec
|
|
||||||
fmt.Println(key, "is an invalid key")
|
|
||||||
return false, crSpec
|
|
||||||
} else if keySub == "" {
|
|
||||||
if key == "rotatekeys" {
|
|
||||||
if _, err := validateInput(value); err != nil {
|
|
||||||
return false, crSpec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// checks if it is git or gitops
|
|
||||||
if keySub != "" {
|
|
||||||
if !keyValid.IsNil() {
|
|
||||||
if !caseInsenstiveFieldByName(reflect.Indirect(keyValid), keySub).IsValid() {
|
|
||||||
fmt.Println(keySub, "is an invalid key")
|
|
||||||
return false, crSpec
|
|
||||||
} else {
|
|
||||||
// verify gitops enabled and gitops schedule
|
|
||||||
switch keySub {
|
|
||||||
case "schedule":
|
|
||||||
if _, err := cron.ParseStandard(value); err != nil {
|
|
||||||
fmt.Println("Please enter string with standard cron scheduling syntax ")
|
|
||||||
return false, crSpec
|
|
||||||
}
|
|
||||||
case "enabled":
|
|
||||||
if !strings.EqualFold(value, "yes") && !strings.EqualFold(value, "no") {
|
|
||||||
fmt.Println("Please use yes or no for key enabled")
|
|
||||||
return false, crSpec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch key {
|
|
||||||
case "gitops":
|
|
||||||
crSpec.Spec.GitOps = &config.GitOps{}
|
|
||||||
case "git":
|
|
||||||
crSpec.Spec.Git = &config.Repo{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, crSpec
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOtherConfigs - set profile/storageclassname/git.repository/manifestRoot commands
|
// SetOtherConfigs - set profile/storageclassname/git.repository/manifestRoot commands
|
||||||
func (q *Qliksense) SetOtherConfigs(args []string) error {
|
func (q *Qliksense) SetOtherConfigs(args []string) error {
|
||||||
// retieve current context from config.yaml
|
// retieve current context from config.yaml
|
||||||
@@ -240,16 +193,12 @@ func (q *Qliksense) SetOtherConfigs(args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if strings.HasPrefix(arg, "fetchSource.") {
|
if strings.HasPrefix(arg, "git.") {
|
||||||
if err := q.processSetFetchSource(arg, qliksenseCR); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if strings.HasPrefix(arg, "git.") {
|
|
||||||
if err := q.processSetGit(arg, qliksenseCR); err != nil {
|
if err := q.processSetGit(arg, qliksenseCR); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if strings.HasPrefix(arg, "gitOps.") {
|
} else if strings.HasPrefix(arg, "opsRunner.") {
|
||||||
if err := q.processSetGitOps(arg, qliksenseCR); err != nil {
|
if err := q.processSetOpsRunner(arg, qliksenseCR); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -273,91 +222,61 @@ func processSetSingleArg(arg string, cr *api.QliksenseCR) error {
|
|||||||
cr.Spec.Profile = nv[1]
|
cr.Spec.Profile = nv[1]
|
||||||
case "storageClassName":
|
case "storageClassName":
|
||||||
cr.Spec.StorageClassName = nv[1]
|
cr.Spec.StorageClassName = nv[1]
|
||||||
case "rotateKeys":
|
|
||||||
valid := false
|
|
||||||
for _, v := range []string{"yes", "no", "None"} {
|
|
||||||
if nv[1] == v {
|
|
||||||
valid = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !valid {
|
|
||||||
return errors.New("please povide rotateKeys=yes|no|None")
|
|
||||||
}
|
|
||||||
cr.Spec.RotateKeys = nv[1]
|
|
||||||
default:
|
default:
|
||||||
return errors.New("Please enter one of: profile, storageClassName,rotateKeys, manifestRoot to configure the current context")
|
return errors.New("Please enter one of: profile, storageClassName, manifestRoot, git to configure the current context")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Qliksense) processSetFetchSource(arg string, cr *api.QliksenseCR) error {
|
func (q *Qliksense) processSetGit(arg string, cr *api.QliksenseCR) error {
|
||||||
args := strings.Split(arg, "=")
|
s := strings.Split(arg, "=")
|
||||||
subs := strings.Split(args[0], ".")
|
tArg0 := strings.TrimSpace(s[0])
|
||||||
if cr.Spec.FetchSource == nil {
|
tArg1 := strings.TrimSpace(s[1])
|
||||||
cr.Spec.FetchSource = &config.Repo{}
|
subs := strings.Split(tArg0, ".")
|
||||||
|
if cr.Spec.Git == nil {
|
||||||
|
cr.Spec.Git = &config.Repo{}
|
||||||
}
|
}
|
||||||
switch subs[1] {
|
switch subs[1] {
|
||||||
case "repository":
|
case "repository":
|
||||||
cr.Spec.FetchSource.Repository = args[1]
|
cr.Spec.Git.Repository = tArg1
|
||||||
case "accessToken":
|
case "accessToken":
|
||||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||||
key, err := qConfig.GetEncryptionKeyFor(cr.GetName())
|
key, err := qConfig.GetEncryptionKeyFor(cr.GetName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return cr.SetFetchAccessToken(args[1], key)
|
return cr.SetFetchAccessToken(tArg1, key)
|
||||||
case "secretName":
|
case "secretName":
|
||||||
cr.Spec.FetchSource.SecretName = args[1]
|
cr.Spec.Git.SecretName = tArg1
|
||||||
case "userName":
|
case "userName":
|
||||||
cr.Spec.FetchSource.UserName = args[1]
|
cr.Spec.Git.UserName = tArg1
|
||||||
default:
|
default:
|
||||||
return errors.New(arg + " does not match any cr spec")
|
return errors.New(arg + " does not match any cr spec")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Qliksense) processSetGit(arg string, cr *api.QliksenseCR) error {
|
func (q *Qliksense) processSetOpsRunner(arg string, cr *api.QliksenseCR) error {
|
||||||
args := strings.Split(arg, "=")
|
args := strings.Split(arg, "=")
|
||||||
subs := strings.Split(args[0], ".")
|
subs := strings.Split(args[0], ".")
|
||||||
if cr.Spec.Git == nil {
|
if cr.Spec.OpsRunner == nil {
|
||||||
cr.Spec.Git = &config.Repo{}
|
cr.Spec.OpsRunner = &config.OpsRunner{}
|
||||||
}
|
|
||||||
switch subs[1] {
|
|
||||||
case "repository":
|
|
||||||
cr.Spec.Git.Repository = args[1]
|
|
||||||
case "accessToken":
|
|
||||||
cr.Spec.Git.AccessToken = args[1]
|
|
||||||
case "secretName":
|
|
||||||
cr.Spec.Git.SecretName = args[1]
|
|
||||||
case "userName":
|
|
||||||
cr.Spec.Git.UserName = args[1]
|
|
||||||
default:
|
|
||||||
return errors.New(arg + " does not match any cr spec")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Qliksense) processSetGitOps(arg string, cr *api.QliksenseCR) error {
|
|
||||||
args := strings.Split(arg, "=")
|
|
||||||
subs := strings.Split(args[0], ".")
|
|
||||||
if cr.Spec.Git == nil {
|
|
||||||
cr.Spec.GitOps = &config.GitOps{}
|
|
||||||
}
|
}
|
||||||
switch subs[1] {
|
switch subs[1] {
|
||||||
case "enabled":
|
case "enabled":
|
||||||
if args[1] != "yes" && args[1] != "no" {
|
if args[1] != "yes" && args[1] != "no" {
|
||||||
return errors.New("Please use yes or no for key enabled")
|
return errors.New("Please use yes or no for key enabled")
|
||||||
}
|
}
|
||||||
cr.Spec.GitOps.Enabled = args[1]
|
cr.Spec.OpsRunner.Enabled = args[1]
|
||||||
case "schedule":
|
case "schedule":
|
||||||
if _, err := cron.ParseStandard(args[1]); err != nil {
|
if _, err := cron.ParseStandard(args[1]); err != nil {
|
||||||
return errors.New("Please enter string with standard cron scheduling syntax ")
|
return errors.New("Please enter string with standard cron scheduling syntax ")
|
||||||
}
|
}
|
||||||
cr.Spec.GitOps.Schedule = args[1]
|
cr.Spec.OpsRunner.Schedule = args[1]
|
||||||
case "watchBranch":
|
case "watchBranch":
|
||||||
cr.Spec.GitOps.WatchBranch = args[1]
|
cr.Spec.OpsRunner.WatchBranch = args[1]
|
||||||
case "image":
|
case "image":
|
||||||
cr.Spec.GitOps.Image = args[1]
|
cr.Spec.OpsRunner.Image = args[1]
|
||||||
default:
|
default:
|
||||||
return errors.New(arg + " does not match any cr spec")
|
return errors.New(arg + " does not match any cr spec")
|
||||||
}
|
}
|
||||||
@@ -411,7 +330,7 @@ func (q *Qliksense) DeleteContextConfig(args []string, flag bool) error {
|
|||||||
out := ansi.NewColorableStdout()
|
out := ansi.NewColorableStdout()
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case qliksenseConfig.Spec.CurrentContext:
|
case qliksenseConfig.Spec.CurrentContext:
|
||||||
fmt.Fprintln(out,Yellow("Please switch contexts to be able to delete this context."))
|
fmt.Fprintln(out, Yellow("Please switch contexts to be able to delete this context."))
|
||||||
err := fmt.Errorf(Red("Cannot delete current context - %s").String(), White(Bold(qliksenseConfig.Spec.CurrentContext)))
|
err := fmt.Errorf(Red("Cannot delete current context - %s").String(), White(Bold(qliksenseConfig.Spec.CurrentContext)))
|
||||||
return err
|
return err
|
||||||
case DefaultQliksenseContext:
|
case DefaultQliksenseContext:
|
||||||
@@ -452,7 +371,7 @@ func (q *Qliksense) DeleteContextConfig(args []string, flag bool) error {
|
|||||||
if ans == true {
|
if ans == true {
|
||||||
api.WriteToFile(&qliksenseConfig, qliksenseConfigFile)
|
api.WriteToFile(&qliksenseConfig, qliksenseConfigFile)
|
||||||
fmt.Fprintln(out, Yellow(Underline("Warning: Active resources may still be running in-cluster")))
|
fmt.Fprintln(out, Yellow(Underline("Warning: Active resources may still be running in-cluster")))
|
||||||
fmt.Fprintln(out, Green("Successfully deleted context: "),Bold(args[0]))
|
fmt.Fprintln(out, Green("Successfully deleted context: "), Bold(args[0]))
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -519,8 +438,8 @@ func (q *Qliksense) SetUpQliksenseContext(contextName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the encrypted default mongo
|
// set the encrypted default mongo for the context in current CR
|
||||||
return q.SetSecrets([]string{`qliksense.mongoDbUri="mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"`}, false, false)
|
return q.SetSecrets([]string{fmt.Sprintf("qliksense.mongodbUri=mongodb://%s-mongodb:27017/qliksense?ssl=false", contextName)}, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateInput(input string) (string, error) {
|
func validateInput(input string) (string, error) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const (
|
|||||||
var targetFileStringTemplate = `
|
var targetFileStringTemplate = `
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
data:
|
data:
|
||||||
mongoDbUri: %s
|
mongodbUri: %s
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: testctx-qliksense-senseinstaller
|
name: testctx-qliksense-senseinstaller
|
||||||
@@ -96,7 +96,6 @@ metadata:
|
|||||||
name: qlik-default
|
name: qlik-default
|
||||||
spec:
|
spec:
|
||||||
profile: docker-desktop
|
profile: docker-desktop
|
||||||
rotateKeys: "yes"
|
|
||||||
releaseName: qlik-default
|
releaseName: qlik-default
|
||||||
`
|
`
|
||||||
qlikDefaultContext := "qlik-default"
|
qlikDefaultContext := "qlik-default"
|
||||||
@@ -244,7 +243,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
|||||||
q: &Qliksense{
|
q: &Qliksense{
|
||||||
QliksenseHome: testDir,
|
QliksenseHome: testDir,
|
||||||
},
|
},
|
||||||
args: []string{"profile=minikube", "rotateKeys=yes", "storageClassName=efs", "gitOps.enabled=yes", "gitOps.schedule=30 * * * *", "git.repository=master", "git.userName=foo", "git.accessToken=1234"},
|
args: []string{"profile=minikube", "storageClassName=efs", "opsRunner.enabled=yes", "opsRunner.schedule=30 * * * *", "git.repository=master", "git.userName=foo", "git.accessToken=1234"},
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
@@ -254,7 +253,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
|||||||
q: &Qliksense{
|
q: &Qliksense{
|
||||||
QliksenseHome: testDir,
|
QliksenseHome: testDir,
|
||||||
},
|
},
|
||||||
args: []string{"someconfig=somevalue, gitOps.schedule=bar", "gitOps.enabled=bar", "git.foo=bar", "rotateKeys=bar"},
|
args: []string{"someconfig=somevalue, opsRunner.schedule=bar", "opsRunner.enabled=bar", "git.foo=bar"},
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -296,7 +295,7 @@ func TestSetConfigs(t *testing.T) {
|
|||||||
q: &Qliksense{
|
q: &Qliksense{
|
||||||
QliksenseHome: testDir,
|
QliksenseHome: testDir,
|
||||||
},
|
},
|
||||||
args: []string{"qliksense.acceptEULA=\"yes\"", "qliksense.mongoDbUri=\"mongo://mongo:3307\""},
|
args: []string{"qliksense.acceptEULA=\"yes\"", "qliksense.mongodbUri=\"mongo://mongo:3307\""},
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
@@ -572,7 +571,7 @@ func Test_SetSecrets(t *testing.T) {
|
|||||||
QliksenseHome: testDir,
|
QliksenseHome: testDir,
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
args: []string{"qliksense.mongoDbUri=\"mongodb://qlik-default-mongodb:27017/qliksense?ssl=false\""},
|
args: []string{"qliksense.mongodbUri=\"mongodb://qlik-default-mongodb:27017/qliksense?ssl=false\""},
|
||||||
isSecretSet: false,
|
isSecretSet: false,
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
@@ -583,7 +582,7 @@ func Test_SetSecrets(t *testing.T) {
|
|||||||
QliksenseHome: testDir,
|
QliksenseHome: testDir,
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
args: []string{"qliksense.mongoDbUri=bW9uZ29kYjovL3FsaWstZGVmYXVsdC1tb25nb2RiOjI3MDE3L3FsaWtzZW5zZT9zc2w9ZmFsc2U="},
|
args: []string{"qliksense.mongodbUri=bW9uZ29kYjovL3FsaWstZGVmYXVsdC1tb25nb2RiOjI3MDE3L3FsaWtzZW5zZT9zc2w9ZmFsc2U="},
|
||||||
isSecretSet: false,
|
isSecretSet: false,
|
||||||
base64: true,
|
base64: true,
|
||||||
},
|
},
|
||||||
@@ -595,7 +594,7 @@ func Test_SetSecrets(t *testing.T) {
|
|||||||
QliksenseHome: testDir,
|
QliksenseHome: testDir,
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
args: []string{"qliksense.mongoDbUri=\"mongo://mongo:3307\""},
|
args: []string{"qliksense.mongodbUri=\"mongo://mongo:3307\""},
|
||||||
isSecretSet: true,
|
isSecretSet: true,
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
@@ -606,7 +605,7 @@ func Test_SetSecrets(t *testing.T) {
|
|||||||
QliksenseHome: testDir,
|
QliksenseHome: testDir,
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
args: []string{"qliksense.mongoDbUri=\"mongodb://qlik-default-mongodb:27017/qliksense?ssl=false\""},
|
args: []string{"qliksense.mongodbUri=\"mongodb://qlik-default-mongodb:27017/qliksense?ssl=false\""},
|
||||||
isSecretSet: true,
|
isSecretSet: true,
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
@@ -744,7 +743,6 @@ metadata:
|
|||||||
name: qlik-default
|
name: qlik-default
|
||||||
spec:
|
spec:
|
||||||
profile: docker-desktop
|
profile: docker-desktop
|
||||||
rotateKeys: "yes"
|
|
||||||
releaseName: qlik-default
|
releaseName: qlik-default
|
||||||
`
|
`
|
||||||
qlikDefaultContext := "qlik-default"
|
qlikDefaultContext := "qlik-default"
|
||||||
@@ -763,7 +761,6 @@ metadata:
|
|||||||
name: qlik1
|
name: qlik1
|
||||||
spec:
|
spec:
|
||||||
profile: docker-desktop
|
profile: docker-desktop
|
||||||
rotateKeys: "yes"
|
|
||||||
releaseName: qlik1`
|
releaseName: qlik1`
|
||||||
|
|
||||||
contextYaml2 :=
|
contextYaml2 :=
|
||||||
@@ -774,7 +771,6 @@ metadata:
|
|||||||
name: qlik2
|
name: qlik2
|
||||||
spec:
|
spec:
|
||||||
profile: docker-desktop
|
profile: docker-desktop
|
||||||
rotateKeys: "yes"
|
|
||||||
releaseName: qlik2`
|
releaseName: qlik2`
|
||||||
|
|
||||||
contextsDir := filepath.Join(testDir, contexts, "qlik1")
|
contextsDir := filepath.Join(testDir, contexts, "qlik1")
|
||||||
|
|||||||
128
pkg/qliksense/context_configs_unset.go
Normal file
128
pkg/qliksense/context_configs_unset.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package qliksense
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
kconfig "github.com/qlik-oss/k-apis/pkg/config"
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (q *Qliksense) UnsetCmd(args []string) error {
|
||||||
|
return unsetAll(q.QliksenseHome, args)
|
||||||
|
}
|
||||||
|
func unsetAll(qHome string, args []string) error {
|
||||||
|
qConfig := api.NewQConfig(qHome)
|
||||||
|
|
||||||
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// either delete all args or none
|
||||||
|
for _, arg := range args {
|
||||||
|
isRemoved := false
|
||||||
|
// delete if key present
|
||||||
|
if !strings.Contains(arg, ".") {
|
||||||
|
if isRemoved = unsetOnlyKey(arg, qcr); isRemoved {
|
||||||
|
//continue to the next arg
|
||||||
|
continue
|
||||||
|
} else if isRemoved = unsetServiceName(arg, qcr); isRemoved {
|
||||||
|
//continue to the next arg
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("%s not found in the context", arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// delete key inside configs if present
|
||||||
|
// delete key inside secrets if present
|
||||||
|
if isRemoved = unsetServiceKey(arg, qcr); isRemoved {
|
||||||
|
//return qConfig.WriteCR(qcr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if isRemoved = unsetTopAttrKey(arg, qcr); !isRemoved {
|
||||||
|
return fmt.Errorf("%s not found in the context", arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return qConfig.WriteCR(qcr)
|
||||||
|
}
|
||||||
|
func unsetOnlyKey(key string, qcr *api.QliksenseCR) bool {
|
||||||
|
|
||||||
|
v := reflect.ValueOf(qcr.Spec).Elem().FieldByName(strings.Title(key))
|
||||||
|
if v.IsValid() && v.CanSet() {
|
||||||
|
v.Set(reflect.Zero(v.Type()))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func unsetServiceName(svc string, qcr *api.QliksenseCR) bool {
|
||||||
|
if qcr.Spec.Configs != nil && qcr.Spec.Configs[svc] != nil {
|
||||||
|
delete(qcr.Spec.Configs, svc)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if qcr.Spec.Secrets != nil && qcr.Spec.Secrets[svc] != nil {
|
||||||
|
delete(qcr.Spec.Secrets, svc)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func unsetServiceKey(svcKey string, qcr *api.QliksenseCR) bool {
|
||||||
|
sk := strings.Split(svcKey, ".")
|
||||||
|
svc := sk[0]
|
||||||
|
key := sk[1]
|
||||||
|
|
||||||
|
if qcr.Spec.Configs != nil && qcr.Spec.Configs[svc] != nil {
|
||||||
|
index := findIndex(key, qcr.Spec.Configs[svc])
|
||||||
|
if index > -1 {
|
||||||
|
qcr.Spec.Configs[svc][index] = qcr.Spec.Configs[svc][len(qcr.Spec.Configs[svc])-1]
|
||||||
|
qcr.Spec.Configs[svc] = qcr.Spec.Configs[svc][:len(qcr.Spec.Configs[svc])-1]
|
||||||
|
if len(qcr.Spec.Configs[svc]) == 0 {
|
||||||
|
delete(qcr.Spec.Configs, svc)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if qcr.Spec.Secrets != nil && qcr.Spec.Secrets[svc] != nil {
|
||||||
|
index := findIndex(key, qcr.Spec.Secrets[svc])
|
||||||
|
if index > -1 {
|
||||||
|
qcr.Spec.Secrets[svc][index] = qcr.Spec.Secrets[svc][len(qcr.Spec.Secrets[svc])-1]
|
||||||
|
qcr.Spec.Secrets[svc] = qcr.Spec.Secrets[svc][:len(qcr.Spec.Secrets[svc])-1]
|
||||||
|
if len(qcr.Spec.Secrets[svc]) == 0 {
|
||||||
|
delete(qcr.Spec.Secrets, svc)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func unsetTopAttrKey(attKey string, qcr *api.QliksenseCR) bool {
|
||||||
|
sk := strings.Split(attKey, ".")
|
||||||
|
attStruct := sk[0]
|
||||||
|
key := sk[1]
|
||||||
|
attV := reflect.ValueOf(qcr.Spec).Elem().FieldByName(strings.Title(attStruct))
|
||||||
|
if !attV.IsValid() || attV.IsZero() || attV.IsNil() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
v := attV.Elem().FieldByName(strings.Title(key))
|
||||||
|
if v.IsValid() && v.CanSet() {
|
||||||
|
v.Set(reflect.Zero(v.Type()))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func findIndex(elem string, nvs kconfig.NameValues) int {
|
||||||
|
for i, nv := range nvs {
|
||||||
|
if nv.Name == elem {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
107
pkg/qliksense/context_configs_unset_test.go
Normal file
107
pkg/qliksense/context_configs_unset_test.go
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package qliksense
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
_ "gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnsetAll(t *testing.T) {
|
||||||
|
qHome, _ := ioutil.TempDir("", "")
|
||||||
|
testPepareDir(qHome)
|
||||||
|
defer os.RemoveAll(qHome)
|
||||||
|
//fmt.Print(qHome)
|
||||||
|
args := []string{"qliksense", "qliksense2.acceptEula3", "serviceA.acceptEula", "opsRunner.watchBranch"}
|
||||||
|
//args := []string{"opsRunner"}
|
||||||
|
//args := []string{"opsRunner.watchBranch"}
|
||||||
|
if err := unsetAll(qHome, args); err != nil {
|
||||||
|
t.Log("error during unset", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
qc := api.NewQConfig(qHome)
|
||||||
|
qcr, err := qc.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
t.Log("error while getting current cr", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if qcr.Spec.Configs["qliksense"] != nil {
|
||||||
|
t.Log("qliksense in configs not deleted")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if len(qcr.Spec.Configs["qliksense2"]) != 1 {
|
||||||
|
t.Log("qliksense2.acceptEula3 not deleted")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if qcr.Spec.Configs["serviceA"] != nil {
|
||||||
|
t.Log("serviceA not deleted")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if qcr.Spec.OpsRunner == nil {
|
||||||
|
t.Log("opsRunner not deleted")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if qcr.Spec.OpsRunner.WatchBranch != "" {
|
||||||
|
t.Log("opsRunner.watchBranch not deleted")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPepareDir(qHome string) {
|
||||||
|
|
||||||
|
config :=
|
||||||
|
`
|
||||||
|
apiVersion: config.qlik.com/v1
|
||||||
|
kind: QliksenseConfig
|
||||||
|
metadata:
|
||||||
|
name: qliksenseConfig
|
||||||
|
spec:
|
||||||
|
contexts:
|
||||||
|
- name: qlik-default
|
||||||
|
crFile: contexts/qlik-default/qlik-default.yaml
|
||||||
|
currentContext: qlik-default
|
||||||
|
`
|
||||||
|
configFile := filepath.Join(qHome, "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
|
||||||
|
opsRunner:
|
||||||
|
enabled: "yes"
|
||||||
|
watchBranch: something
|
||||||
|
configs:
|
||||||
|
qliksense:
|
||||||
|
- name: acceptEula
|
||||||
|
value: some
|
||||||
|
qliksense2:
|
||||||
|
- name: acceptEula2
|
||||||
|
value: some
|
||||||
|
- name: acceptEula3
|
||||||
|
value: some
|
||||||
|
serviceA:
|
||||||
|
- name: acceptEula
|
||||||
|
value: some
|
||||||
|
`
|
||||||
|
qlikDefaultContext := "qlik-default"
|
||||||
|
// create contexts/qlik-default/ under tests/
|
||||||
|
contexts := "contexts"
|
||||||
|
contextsDir := filepath.Join(qHome, contexts, qlikDefaultContext)
|
||||||
|
if err := os.MkdirAll(contextsDir, 0777); err != nil {
|
||||||
|
err = fmt.Errorf("Not able to create directories")
|
||||||
|
}
|
||||||
|
|
||||||
|
contextFile := filepath.Join(contextsDir, qlikDefaultContext+".yaml")
|
||||||
|
ioutil.WriteFile(contextFile, []byte(contextYaml), 0777)
|
||||||
|
}
|
||||||
@@ -5,7 +5,15 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
apixv1beta1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CrdCommandOptions struct {
|
type CrdCommandOptions struct {
|
||||||
@@ -20,11 +28,11 @@ func (q *Qliksense) ViewCrds(opts *CrdCommandOptions) error {
|
|||||||
fmt.Println("cannot get the current-context cr", err)
|
fmt.Println("cannot get the current-context cr", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
engineCRD, err := getQliksenseInitCrd(qcr)
|
engineCRD, err := getQliksenseInitCrds(qcr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
customCrd, err := getCustomCrd(qcr)
|
customCrd, err := getCustomCrds(qcr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -51,12 +59,12 @@ func (q *Qliksense) InstallCrds(opts *CrdCommandOptions) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if engineCRD, err := getQliksenseInitCrd(qcr); err != nil {
|
if engineCRD, err := getQliksenseInitCrds(qcr); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err = qapi.KubectlApply(engineCRD, ""); err != nil {
|
} else if err = qapi.KubectlApply(engineCRD, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if customCrd, err := getCustomCrd(qcr); err != nil {
|
if customCrd, err := getCustomCrds(qcr); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if customCrd != "" {
|
} else if customCrd != "" {
|
||||||
if err = qapi.KubectlApply(customCrd, ""); err != nil {
|
if err = qapi.KubectlApply(customCrd, ""); err != nil {
|
||||||
@@ -73,7 +81,7 @@ func (q *Qliksense) InstallCrds(opts *CrdCommandOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getQliksenseInitCrd(qcr *qapi.QliksenseCR) (string, error) {
|
func getQliksenseInitCrds(qcr *qapi.QliksenseCR) (string, error) {
|
||||||
var repoPath string
|
var repoPath string
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -85,11 +93,15 @@ func getQliksenseInitCrd(qcr *qapi.QliksenseCR) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qInitMsPath := filepath.Join(repoPath, Q_INIT_CRD_PATH)
|
qInitMsPath := filepath.Join(repoPath, "manifests", qcr.Spec.Profile, "crds")
|
||||||
if _, err := os.Lstat(qInitMsPath); err != nil {
|
if _, err := os.Lstat(qInitMsPath); err != nil {
|
||||||
// older version of qliksense-init used
|
qInitMsPath = filepath.Join(repoPath, Q_INIT_CRD_PATH)
|
||||||
qInitMsPath = filepath.Join(repoPath, "manifests/base/manifests/qliksense-init")
|
if _, err := os.Lstat(qInitMsPath); err != nil {
|
||||||
|
// older version of qliksense-init used
|
||||||
|
qInitMsPath = filepath.Join(repoPath, "manifests/base/manifests/qliksense-init")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qInitByte, err := ExecuteKustomizeBuild(qInitMsPath)
|
qInitByte, err := ExecuteKustomizeBuild(qInitMsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("cannot generate crds for qliksense-init", err)
|
fmt.Println("cannot generate crds for qliksense-init", err)
|
||||||
@@ -98,7 +110,7 @@ func getQliksenseInitCrd(qcr *qapi.QliksenseCR) (string, error) {
|
|||||||
return string(qInitByte), nil
|
return string(qInitByte), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCustomCrd(qcr *qapi.QliksenseCR) (string, error) {
|
func getCustomCrds(qcr *qapi.QliksenseCR) (string, error) {
|
||||||
crdPath := qcr.GetCustomCrdsPath()
|
crdPath := qcr.GetCustomCrdsPath()
|
||||||
if crdPath == "" {
|
if crdPath == "" {
|
||||||
return "", nil
|
return "", nil
|
||||||
@@ -110,3 +122,77 @@ func getCustomCrd(qcr *qapi.QliksenseCR) (string, error) {
|
|||||||
}
|
}
|
||||||
return string(qInitByte), nil
|
return string(qInitByte), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Qliksense) CheckAllCrdsInstalled() (bool, error) {
|
||||||
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
customResourceDefinitionInterface, err := getCustomResourceDefinitionInterface()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if engineCRDs, err := getQliksenseInitCrds(qcr); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if allInstalled, err := checkCrdsInstalled(engineCRDs, customResourceDefinitionInterface); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if !allInstalled {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if customCrds, err := getCustomCrds(qcr); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if allInstalled, err := checkCrdsInstalled(customCrds, customResourceDefinitionInterface); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if !allInstalled {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if allInstalled, err := checkCrdsInstalled(q.GetOperatorCRDString(), customResourceDefinitionInterface); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if !allInstalled {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCrdsInstalled(crds string, customResourceDefinitionInterface apixv1beta1client.CustomResourceDefinitionInterface) (bool, error) {
|
||||||
|
kuzResourceFactory := resmap.NewFactory(resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()), nil)
|
||||||
|
if kuzResMap, err := kuzResourceFactory.NewResMapFromBytes([]byte(crds)); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else {
|
||||||
|
for _, kuzRes := range kuzResMap.Resources() {
|
||||||
|
if customResourceDefinition, err := customResourceDefinitionInterface.Get(kuzRes.GetName(), v1.GetOptions{}); err != nil && apierrors.IsNotFound(err) {
|
||||||
|
return false, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if customResourceDefinition == nil {
|
||||||
|
return false, fmt.Errorf("failed looking up crd: %v", kuzRes.GetName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCustomResourceDefinitionInterface() (apixv1beta1client.CustomResourceDefinitionInterface, error) {
|
||||||
|
homeDir, err := homedir.Dir()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
kubeconfigPath := filepath.Join(homeDir, ".kube", "config")
|
||||||
|
k8sRestConfig, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
apixClient, err := apixv1beta1client.NewForConfig(k8sRestConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return apixClient.CustomResourceDefinitions(), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
package qliksense
|
package qliksense
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
apixv1beta1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
|
||||||
|
"github.com/gobuffalo/packr/v2"
|
||||||
|
|
||||||
kapi_config "github.com/qlik-oss/k-apis/pkg/config"
|
kapi_config "github.com/qlik-oss/k-apis/pkg/config"
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
)
|
)
|
||||||
@@ -13,7 +23,7 @@ func TestGetQliksenseInitCrd(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
crdFromContextConfig, err := getQliksenseInitCrd(&qapi.QliksenseCR{
|
crdFromContextConfig, err := getQliksenseInitCrds(&qapi.QliksenseCR{
|
||||||
KApiCr: kapi_config.KApiCr{
|
KApiCr: kapi_config.KApiCr{
|
||||||
Spec: &kapi_config.CRSpec{
|
Spec: &kapi_config.CRSpec{
|
||||||
ManifestsRoot: someTmpRepoPath,
|
ManifestsRoot: someTmpRepoPath,
|
||||||
@@ -24,7 +34,7 @@ func TestGetQliksenseInitCrd(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
crdFromDownloadedConfig, err := getQliksenseInitCrd(&qapi.QliksenseCR{
|
crdFromDownloadedConfig, err := getQliksenseInitCrds(&qapi.QliksenseCR{
|
||||||
KApiCr: kapi_config.KApiCr{
|
KApiCr: kapi_config.KApiCr{
|
||||||
Spec: &kapi_config.CRSpec{
|
Spec: &kapi_config.CRSpec{
|
||||||
ManifestsRoot: "",
|
ManifestsRoot: "",
|
||||||
@@ -39,3 +49,87 @@ func TestGetQliksenseInitCrd(t *testing.T) {
|
|||||||
t.Fatalf("expected %v to equal %v, but they didn't", crdFromContextConfig, crdFromDownloadedConfig)
|
t.Fatalf("expected %v to equal %v, but they didn't", crdFromContextConfig, crdFromDownloadedConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCheckAllCrdsInstalled(t *testing.T) {
|
||||||
|
t.Skip("Skipping this test because it makes kubernetes calls")
|
||||||
|
|
||||||
|
tmpQlikSenseHome, err := ioutil.TempDir("", "tmp-qlik-sense-home-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error creating tmp dir: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpQlikSenseHome)
|
||||||
|
|
||||||
|
setupQliksenseTestDefaultContext(t, tmpQlikSenseHome, `
|
||||||
|
apiVersion: qlik.com/v1
|
||||||
|
kind: Qliksense
|
||||||
|
metadata:
|
||||||
|
name: qlik-default
|
||||||
|
spec:
|
||||||
|
profile: docker-desktop
|
||||||
|
`)
|
||||||
|
|
||||||
|
q := &Qliksense{
|
||||||
|
QliksenseHome: tmpQlikSenseHome,
|
||||||
|
CrdBox: packr.New("crds", "./crds"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.FetchQK8s("v1.50.3"); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if allInstalled, err := q.CheckAllCrdsInstalled(); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
} else if allInstalled {
|
||||||
|
t.Fatal("expected crds to NOT be installed at this point")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.InstallCrds(&CrdCommandOptions{All: true}); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
} else if allInstalled, err := q.CheckAllCrdsInstalled(); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
} else if !allInstalled {
|
||||||
|
t.Fatal("expected crds to BE installed at this point")
|
||||||
|
}
|
||||||
|
|
||||||
|
//cleanup:
|
||||||
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
customResourceDefinitionInterface, err := getCustomResourceDefinitionInterface()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if engineCRDs, err := getQliksenseInitCrds(qcr); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
} else if err := deleteCrds(engineCRDs, customResourceDefinitionInterface); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if customCrd, err := getCustomCrds(qcr); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
} else if err := deleteCrds(customCrd, customResourceDefinitionInterface); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := deleteCrds(q.GetOperatorCRDString(), customResourceDefinitionInterface); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteCrds(crds string, customResourceDefinitionInterface apixv1beta1client.CustomResourceDefinitionInterface) error {
|
||||||
|
kuzResourceFactory := resmap.NewFactory(resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()), nil)
|
||||||
|
if kuzResMap, err := kuzResourceFactory.NewResMapFromBytes([]byte(crds)); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
for _, kuzRes := range kuzResMap.Resources() {
|
||||||
|
if err := customResourceDefinitionInterface.Delete(kuzRes.GetName(), &v1.DeleteOptions{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,19 +30,14 @@ const (
|
|||||||
|
|
||||||
func (q *Qliksense) PullImages(version, profile string) error {
|
func (q *Qliksense) PullImages(version, profile string) error {
|
||||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
if version != "" {
|
|
||||||
if !qConfig.IsRepoExistForCurrent(version) {
|
|
||||||
if err := q.FetchQK8s(version); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qcr, err := qConfig.GetCurrentCR()
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !qcr.IsRepoExist() {
|
if !qcr.IsRepoExist() {
|
||||||
return errors.New("ManifestsRoot not found")
|
if err := fetchAndUpdateCR(qConfig, version); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if profile != "" {
|
if profile != "" {
|
||||||
qcr.Spec.Profile = profile
|
qcr.Spec.Profile = profile
|
||||||
@@ -95,9 +90,9 @@ func (q *Qliksense) PullImagesForCurrentCR() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Qliksense) appendGitOpsImage(images *[]string, qcr *qapi.QliksenseCR) {
|
func (q *Qliksense) appendOpsRunnerImage(images *[]string, qcr *qapi.QliksenseCR) {
|
||||||
if qcr.Spec.GitOps != nil && qcr.Spec.GitOps.Image != "" {
|
if qcr.Spec.OpsRunner != nil && qcr.Spec.OpsRunner.Image != "" {
|
||||||
*images = append(*images, qcr.Spec.GitOps.Image)
|
*images = append(*images, qcr.Spec.OpsRunner.Image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +155,10 @@ func (q *Qliksense) PushImagesForCurrentCR() error {
|
|||||||
qcr, err := qConfig.GetCurrentCR()
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
} else if err := ensureImageRegistrySetInCR(qcr); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
version := qcr.GetLabelFromCr("version")
|
version := qcr.GetLabelFromCr("version")
|
||||||
profile := qcr.Spec.Profile
|
profile := qcr.Spec.Profile
|
||||||
repoDir := qcr.Spec.ManifestsRoot
|
repoDir := qcr.Spec.ManifestsRoot
|
||||||
@@ -212,7 +210,7 @@ func (q *Qliksense) appendAdditionalImages(images *[]string, qcr *qapi.Qliksense
|
|||||||
if err := q.appendOperatorImages(images); err != nil {
|
if err := q.appendOperatorImages(images); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
q.appendGitOpsImage(images, qcr)
|
q.appendOpsRunnerImage(images, qcr)
|
||||||
q.appendPreflightImages(images)
|
q.appendPreflightImages(images)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -344,3 +342,20 @@ func (q *Qliksense) writeVersionOutput(versionOut *VersionOutput, imagesDir, ver
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validatePullPushFlagsOnInstall(cr *qapi.QliksenseCR, pull, push bool) error {
|
||||||
|
if pull && !push {
|
||||||
|
fmt.Printf("WARNING: pulling images without pushing them\n")
|
||||||
|
}
|
||||||
|
if push {
|
||||||
|
return ensureImageRegistrySetInCR(cr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureImageRegistrySetInCR(cr *qapi.QliksenseCR) error {
|
||||||
|
if registry := cr.Spec.GetImageRegistry(); registry == "" {
|
||||||
|
return errors.New("no image registry set in the CR; to set it use: qliksense config set-image-registry")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ kind: Qliksense
|
|||||||
metadata:
|
metadata:
|
||||||
name: qlik-default
|
name: qlik-default
|
||||||
spec:
|
spec:
|
||||||
gitOps:
|
opsRunner:
|
||||||
image: some-gitops-image
|
image: some-gitops-image
|
||||||
`)
|
`)
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ spec:
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !haveMatchingImage(func(image string) bool {
|
if !haveMatchingImage(func(image string) bool {
|
||||||
return strings.Contains(image, "qlik-docker-oss.bintray.io/qliksense-operator:v")
|
return strings.Contains(image, "qlik-docker-oss.bintray.io/qliksense-operator:")
|
||||||
}) {
|
}) {
|
||||||
t.Fatal("expected to find the operator image in the list, but it wasn't there")
|
t.Fatal("expected to find the operator image in the list, but it wasn't there")
|
||||||
}
|
}
|
||||||
@@ -235,19 +235,19 @@ spec:
|
|||||||
t.Fatal("expected to find the GitOps image in the list, but it wasn't there")
|
t.Fatal("expected to find the GitOps image in the list, but it wasn't there")
|
||||||
}
|
}
|
||||||
if !haveMatchingImage(func(image string) bool {
|
if !haveMatchingImage(func(image string) bool {
|
||||||
return image == "nginx"
|
return strings.Contains(image, "nginx")
|
||||||
}) {
|
}) {
|
||||||
t.Fatal("expected to find the nginx Preflight image in the list, but it wasn't there")
|
t.Fatal("expected to find the nginx Preflight image in the list, but it wasn't there")
|
||||||
}
|
}
|
||||||
if !haveMatchingImage(func(image string) bool {
|
if !haveMatchingImage(func(image string) bool {
|
||||||
return image == "subfuzion/netcat"
|
return strings.Contains(image, "preflight-netcat")
|
||||||
}) {
|
}) {
|
||||||
t.Fatal("expected to find the netcat Preflight image in the list, but it wasn't there")
|
t.Fatal("expected to find the netcat Preflight image in the list, but it wasn't there")
|
||||||
}
|
}
|
||||||
if !haveMatchingImage(func(image string) bool {
|
if !haveMatchingImage(func(image string) bool {
|
||||||
return image == "mongo"
|
return strings.Contains(image, "qlik-docker-oss.bintray.io/preflight-mongo")
|
||||||
}) {
|
}) {
|
||||||
t.Fatal("expected to find the mongo Preflight image in the list, but it wasn't there")
|
t.Fatal("expected to find the preflight-mongo image in the list, but it wasn't there")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,7 +268,6 @@ spec:
|
|||||||
- name: imageRegistry
|
- name: imageRegistry
|
||||||
value: %s
|
value: %s
|
||||||
manifestsRoot: %s
|
manifestsRoot: %s
|
||||||
rotateKeys: "yes"
|
|
||||||
releaseName: qlik-default
|
releaseName: qlik-default
|
||||||
`, version, registry.url, manifestsRootDir)
|
`, version, registry.url, manifestsRootDir)
|
||||||
setupQliksenseTestDefaultContext(t, tmpQlikSenseHome, cr)
|
setupQliksenseTestDefaultContext(t, tmpQlikSenseHome, cr)
|
||||||
@@ -315,23 +314,23 @@ spec:
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
transformersDir := path.Join(manifestsRootDir, "transformers")
|
transformersDir := path.Join(manifestsRootDir, "manifests", "base", "transformers", "release")
|
||||||
if err := os.MkdirAll(transformersDir, os.ModePerm); err != nil {
|
if err := os.MkdirAll(transformersDir, os.ModePerm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(path.Join(transformersDir, "qseokversion.yaml"), []byte(`
|
if err := ioutil.WriteFile(path.Join(transformersDir, "annotations.yaml"), []byte(`
|
||||||
apiVersion: qlik.com/v1
|
apiVersion: builtin
|
||||||
kind: SelectivePatch
|
kind: AnnotationsTransformer
|
||||||
metadata:
|
metadata:
|
||||||
name: qseokversion
|
name: common-annotations
|
||||||
enabled: true
|
annotations:
|
||||||
patches:
|
app.kubernetes.io/name: qliksense
|
||||||
- target:
|
app.kubernetes.io/instance: $(PREFIX)
|
||||||
kind: HelmChart
|
app.kubernetes.io/version: 1.21.23
|
||||||
labelSelector: name!=qliksense-init
|
app.kubernetes.io/managed-by: qliksense-operator
|
||||||
patch: |-
|
fieldSpecs:
|
||||||
chartName: qliksense
|
- path: metadata/annotations
|
||||||
chartVersion: 1.21.23
|
create: true
|
||||||
`), os.ModePerm); err != nil {
|
`), os.ModePerm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
package qliksense
|
|
||||||
|
|
||||||
import (
|
|
||||||
"archive/zip"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (q *Qliksense) ExportContext(context string, output string) error {
|
|
||||||
qliksenseContextsDir := filepath.Join(q.QliksenseHome, QliksenseContextsDir)
|
|
||||||
qliksenseContextFile := filepath.Join(qliksenseContextsDir, context)
|
|
||||||
qliksenseSecretsDir := filepath.Join(q.QliksenseHome, QliksenseSecretsDir, QliksenseContextsDir)
|
|
||||||
qliksenseSecretsFile := filepath.Join(qliksenseSecretsDir, context)
|
|
||||||
// files := []string{qliksenseContextFile, qliksenseSecretsFile}
|
|
||||||
|
|
||||||
fmt.Println(q.QliksenseHome)
|
|
||||||
fmt.Println(qliksenseSecretsFile)
|
|
||||||
fmt.Println(qliksenseContextFile)
|
|
||||||
|
|
||||||
filename := "result.zip"
|
|
||||||
destinationFile, err := os.Create(output + "/" + filename)
|
|
||||||
var folders []string
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
folders = append(folders, qliksenseContextFile, qliksenseSecretsFile)
|
|
||||||
if err := RecursiveZip(folders, destinationFile); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func RecursiveZip(pathToZip []string, destinationFile *os.File) error {
|
|
||||||
s myZip := zip.NewWriter(destinationFile)
|
|
||||||
for _, element := range pathToZip {
|
|
||||||
err := filepath.Walk(element, func(filePath string, info os.FileInfo, err error) error {
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
relPath := strings.TrimPrefix(filePath, element)
|
|
||||||
zipFile, err := myZip.Create(relPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fsFile, err := os.Open(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = io.Copy(zipFile, fsFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})xs
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err := myZip.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ package qliksense
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@@ -54,10 +53,7 @@ func (q *Qliksense) FetchK8sWithOpts(opts *FetchCommandOptions) error {
|
|||||||
cr.SetFetchUrl(opts.GitUrl)
|
cr.SetFetchUrl(opts.GitUrl)
|
||||||
}
|
}
|
||||||
v := getVersion(opts, cr)
|
v := getVersion(opts, cr)
|
||||||
if v == "" {
|
if v != "" && qConfig.IsRepoExistForCurrent(v) {
|
||||||
return errors.New("Cannot find gitref/tag/branch/version to fetch")
|
|
||||||
}
|
|
||||||
if qConfig.IsRepoExistForCurrent(v) {
|
|
||||||
if opts.Overwrite || getVerionsOverwriteConfirmation(v) == "y" {
|
if opts.Overwrite || getVerionsOverwriteConfirmation(v) == "y" {
|
||||||
if err := qConfig.DeleteRepoForCurrent(v); err != nil {
|
if err := qConfig.DeleteRepoForCurrent(v); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -98,7 +94,6 @@ func fetchAndUpdateCR(qConfig *qapi.QliksenseConfig, version string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
destDir := qConfig.BuildRepoPath(version)
|
destDir := qConfig.BuildRepoPath(version)
|
||||||
fmt.Printf("fetching version [%s] from %s\n", version, qcr.GetFetchUrl())
|
fmt.Printf("fetching version [%s] from %s\n", version, qcr.GetFetchUrl())
|
||||||
if err := qapi.CopyDirectory(tempDest, destDir); err != nil {
|
if err := qapi.CopyDirectory(tempDest, destDir); err != nil {
|
||||||
@@ -142,7 +137,7 @@ func getVersion(opts *FetchCommandOptions, qcr *qapi.QliksenseCR) string {
|
|||||||
|
|
||||||
func getVerionsOverwriteConfirmation(version string) string {
|
func getVerionsOverwriteConfirmation(version string) string {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
fmt.Println("The version [" + version + "] already exist")
|
fmt.Println("The version [" + version + "] already exists")
|
||||||
cfm := "n"
|
cfm := "n"
|
||||||
for {
|
for {
|
||||||
fmt.Print("Do you want to delete and fetch again [y/N]: ")
|
fmt.Print("Do you want to delete and fetch again [y/N]: ")
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func TestFetchAndUpdateCR(t *testing.T) {
|
|||||||
}
|
}
|
||||||
q.SetUpQliksenseContext("test1")
|
q.SetUpQliksenseContext("test1")
|
||||||
qConfig := qapi.NewQConfig(tempHome)
|
qConfig := qapi.NewQConfig(tempHome)
|
||||||
if err := fetchAndUpdateCR(qConfig, "v0.0.2"); err != nil {
|
if err := fetchAndUpdateCR(qConfig, "v0.0.8"); err != nil {
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -28,8 +28,8 @@ func TestFetchAndUpdateCR(t *testing.T) {
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if cr.Spec.ManifestsRoot != "contexts/test1/qlik-k8s/v0.0.2" {
|
if cr.Spec.ManifestsRoot != "contexts/test1/qlik-k8s/v0.0.8" {
|
||||||
t.Log("actual path: " + cr.Spec.ManifestsRoot + ", expected path: contexts/test1/qlik-k8s/v0.0.2")
|
t.Log("actual path: " + cr.Spec.ManifestsRoot + ", expected path: contexts/test1/qlik-k8s/v0.0.8")
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
//testing latest tag is fetched
|
//testing latest tag is fetched
|
||||||
@@ -43,7 +43,7 @@ func TestFetchAndUpdateCR(t *testing.T) {
|
|||||||
cr = &qapi.QliksenseCR{}
|
cr = &qapi.QliksenseCR{}
|
||||||
qapi.ReadFromFile(cr, actualCrFile)
|
qapi.ReadFromFile(cr, actualCrFile)
|
||||||
v := cr.GetLabelFromCr("version")
|
v := cr.GetLabelFromCr("version")
|
||||||
if v == "" || v == "v0.0.2" {
|
if v == "" || v == "v0.0.8" {
|
||||||
t.Log("should get latest but got version: " + v)
|
t.Log("should get latest but got version: " + v)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||||
"github.com/qlik-oss/k-apis/pkg/git"
|
"github.com/qlik-oss/k-apis/pkg/git"
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
)
|
)
|
||||||
@@ -22,8 +24,20 @@ func (q *Qliksense) GetInstallableVersions(opts *LsRemoteCmdOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var repoPath string
|
var repoPath string
|
||||||
|
var auth transport.AuthMethod
|
||||||
if qcr.Spec.GetManifestsRoot() != "" {
|
if qcr.Spec.GetManifestsRoot() != "" {
|
||||||
repoPath = qcr.Spec.GetManifestsRoot()
|
repoPath = qcr.Spec.GetManifestsRoot()
|
||||||
|
encKey, err := qConfig.GetEncryptionKeyFor(qcr.GetName())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
accessToken := qcr.GetFetchAccessToken(encKey)
|
||||||
|
if accessToken != "" {
|
||||||
|
auth = &http.BasicAuth{
|
||||||
|
Username: "something",
|
||||||
|
Password: accessToken,
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
repoPath, err = DownloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
|
repoPath, err = DownloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -36,7 +50,7 @@ func (q *Qliksense) GetInstallableVersions(opts *LsRemoteCmdOptions) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteRefsList, err := git.GetRemoteRefs(r, nil,
|
remoteRefsList, err := git.GetRemoteRefs(r, auth,
|
||||||
&git.RemoteRefConstraints{
|
&git.RemoteRefConstraints{
|
||||||
Include: true,
|
Include: true,
|
||||||
Sort: true,
|
Sort: true,
|
||||||
@@ -96,13 +110,18 @@ func getLatestTag(repoUrl, accessToken string) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := git.OpenRepository(repoPath)
|
r, err := git.OpenRepository(repoPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
var auth transport.AuthMethod
|
||||||
remoteRefsList, err := git.GetRemoteRefs(r, nil,
|
if accessToken != "" {
|
||||||
|
auth = &http.BasicAuth{
|
||||||
|
Username: "something",
|
||||||
|
Password: accessToken,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remoteRefsList, err := git.GetRemoteRefs(r, auth,
|
||||||
&git.RemoteRefConstraints{
|
&git.RemoteRefConstraints{
|
||||||
Include: true,
|
Include: true,
|
||||||
Sort: true,
|
Sort: true,
|
||||||
@@ -144,7 +163,7 @@ func getLatestTag(repoUrl, accessToken string) (string, error) {
|
|||||||
v, err := semver.NewVersion(sv)
|
v, err := semver.NewVersion(sv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// it may happen, in the repo some tags may not conform to semver
|
// it may happen, in the repo some tags may not conform to semver
|
||||||
fmt.Print("Unconform tags: " + sv)
|
//fmt.Println("the tag is not conform to semver: " + sv)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if maxSem == nil || maxSem.LessThan(v) {
|
if maxSem == nil || maxSem.LessThan(v) {
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ func TestGetLatestTag(t *testing.T) {
|
|||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.Log(sv)
|
t.Log(sv)
|
||||||
}
|
}
|
||||||
baseV, _ := semver.NewVersion("v0.0.7")
|
baseV, _ := semver.NewVersion("v0.0.8")
|
||||||
if !sv.GreaterThan(baseV) {
|
if !sv.GreaterThan(baseV) {
|
||||||
t.Log("Expected greater than v0.0.7, but got: " + s)
|
t.Log("Expected greater than v0.0.8, but got: " + s)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,61 +7,112 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mattn/go-tty"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
"github.com/qlik-oss/k-apis/pkg/config"
|
"github.com/qlik-oss/k-apis/pkg/config"
|
||||||
|
"github.com/qlik-oss/k-apis/pkg/cr"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InstallCommandOptions struct {
|
type InstallCommandOptions struct {
|
||||||
StorageClass string
|
StorageClass string
|
||||||
MongoDbUri string
|
MongodbUri string
|
||||||
RotateKeys string
|
AcceptEULA string
|
||||||
|
DryRun bool
|
||||||
|
Pull bool
|
||||||
|
Push bool
|
||||||
|
CleanPatchFiles bool
|
||||||
|
RotateKeys bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, keepPatchFiles bool) error {
|
const (
|
||||||
|
eulaText = "Please read the end user license agreement at: https://www.qlik.com/us/legal/license-terms"
|
||||||
|
eulaPrompt = "Do you accept our EULA? (y/n): "
|
||||||
|
eulaErrorInstruction = `You must enter "y" to continue or execute the command with the acceptEULA flag set to "yes"`
|
||||||
|
)
|
||||||
|
|
||||||
// step1: fetch 1.0.0 # pull down qliksense-k8s@1.0.0
|
func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions) error {
|
||||||
// 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)
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
if !keepPatchFiles {
|
|
||||||
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
|
|
||||||
fmt.Printf("error removing temporary changes to the config: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qcr, err := qConfig.GetCurrentCR()
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("cannot get the current-context cr", err)
|
fmt.Println("cannot get the current-context cr", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !qcr.IsRepoExist() {
|
||||||
|
if err := fetchAndUpdateCR(qConfig, version); err != nil {
|
||||||
|
return err
|
||||||
|
} else if qcr, err = qConfig.GetCurrentCR(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.AcceptEULA != "" && opts.AcceptEULA != "yes" {
|
||||||
|
enforceEula()
|
||||||
|
} else if opts.AcceptEULA == "" && !qcr.IsEULA() {
|
||||||
|
enforceEula()
|
||||||
|
}
|
||||||
qcr.SetEULA("yes")
|
qcr.SetEULA("yes")
|
||||||
if opts.MongoDbUri != "" {
|
|
||||||
qcr.Spec.AddToSecrets("qliksense", "mongoDbUri", opts.MongoDbUri, "")
|
if opts.MongodbUri != "" {
|
||||||
|
qcr.Spec.AddToSecrets("qliksense", "mongodbUri", opts.MongodbUri, "")
|
||||||
}
|
}
|
||||||
if opts.StorageClass != "" {
|
if opts.StorageClass != "" {
|
||||||
qcr.Spec.StorageClassName = opts.StorageClass
|
qcr.Spec.StorageClassName = opts.StorageClass
|
||||||
}
|
}
|
||||||
if opts.RotateKeys != "" {
|
|
||||||
qcr.Spec.RotateKeys = opts.RotateKeys
|
|
||||||
}
|
|
||||||
qConfig.WriteCurrentContextCR(qcr)
|
|
||||||
|
|
||||||
//if the docker pull secret exists on disk, install it in the cluster
|
if err := qConfig.WriteCurrentContextCR(qcr); err != nil {
|
||||||
//if it doesn't exist on disk, remove it in the cluster
|
|
||||||
if err := installOrRemoveImagePullSecret(qConfig); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if acceptEULA is yes or not
|
if opts.CleanPatchFiles {
|
||||||
if !qcr.IsEULA() {
|
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
|
||||||
return errors.New(agreementTempalte + "\n Please do $ qliksense install --acceptEULA=yes\n")
|
fmt.Printf("error removing temporary changes to the config: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for debugging purpose
|
||||||
|
if opts.DryRun {
|
||||||
|
// generate patches
|
||||||
|
userHomeDir, _ := homedir.Dir()
|
||||||
|
fmt.Println("Generating patches only")
|
||||||
|
cr.GeneratePatches(&qcr.KApiCr, config.KeysActionDoNothing, path.Join(userHomeDir, ".kube", "config"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if installed, err := q.CheckAllCrdsInstalled(); err != nil {
|
||||||
|
fmt.Println("error verifying whether CRDs are installed", err)
|
||||||
|
return err
|
||||||
|
} else if !installed {
|
||||||
|
return errors.New(`please install CRDs by executing: $ qliksense crds install`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validatePullPushFlagsOnInstall(qcr, opts.Pull, opts.Push); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if opts.Pull {
|
||||||
|
fmt.Println("Pulling images...")
|
||||||
|
if err := q.PullImages(version, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.Push {
|
||||||
|
fmt.Println("Pushing images...")
|
||||||
|
if err := q.PushImagesForCurrentCR(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := applyImagePullSecret(qConfig); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//CRD will be installed outside of operator
|
//CRD will be installed outside of operator
|
||||||
@@ -75,13 +126,25 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// create patch dependent resoruces
|
// create patch dependent resources
|
||||||
fmt.Println("Installing resoruces used kuztomize patch")
|
fmt.Println("Installing resources used by the kuztomize patch")
|
||||||
if err := q.createK8sResoruceBeforePatch(qcr); err != nil {
|
if err := q.createK8sResourceBeforePatch(qcr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if qcr.Spec.Git != nil && qcr.Spec.Git.Repository != "" {
|
if opts.RotateKeys {
|
||||||
|
fmt.Println("Deleting stored application keys")
|
||||||
|
if err := q.DeleteKeysClusterBackup(); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
qcr.AddLabelToCr("keys-rotated", strconv.FormatInt(time.Now().Unix(), 10))
|
||||||
|
if err := qConfig.WriteCurrentContextCR(qcr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if qcr.Spec.OpsRunner != nil {
|
||||||
// fetching and applying manifest will be in the operator controller
|
// fetching and applying manifest will be in the operator controller
|
||||||
// get decrypted cr
|
// get decrypted cr
|
||||||
if dcr, err := qConfig.GetDecryptedCr(qcr); err != nil {
|
if dcr, err := qConfig.GetDecryptedCr(qcr); err != nil {
|
||||||
@@ -90,35 +153,16 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
|||||||
return q.applyCR(dcr)
|
return q.applyCR(dcr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !qcr.IsRepoExist() {
|
|
||||||
if err := fetchAndUpdateCR(qConfig, version); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qcr, err = qConfig.GetCurrentCR()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("cannot get the current-context cr", err)
|
|
||||||
return err
|
|
||||||
} else if qcr.Spec.GetManifestsRoot() == "" {
|
|
||||||
return errors.New("cannot get the manifest root. Use qliksense fetch <version> or qliksense set manifestsRoot")
|
|
||||||
}
|
|
||||||
|
|
||||||
// install generated manifests into cluster
|
// install generated manifests into cluster
|
||||||
fmt.Println("Installing generated manifests into cluster")
|
fmt.Println("Installing generated manifests into the cluster")
|
||||||
|
|
||||||
if dcr, err := qConfig.GetDecryptedCr(qcr); err != nil {
|
if dcr, err := qConfig.GetDecryptedCr(qcr); err != nil {
|
||||||
return err
|
return err
|
||||||
|
} else if err := q.applyConfigToK8s(dcr); err != nil {
|
||||||
|
fmt.Println("cannot do kubectl apply on manifests")
|
||||||
|
return err
|
||||||
} else {
|
} else {
|
||||||
if IsQliksenseInstalled(dcr.GetName()) {
|
return q.applyCR(dcr)
|
||||||
return q.UpgradeQK8s(keepPatchFiles)
|
|
||||||
}
|
|
||||||
if err := q.applyConfigToK8s(dcr); err != nil {
|
|
||||||
fmt.Println("cannot do kubectl apply on manifests")
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
return q.applyCR(dcr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,22 +176,13 @@ func (q *Qliksense) getProcessedOperatorControllerString(qcr *qapi.QliksenseCR)
|
|||||||
return operatorControllerString, nil
|
return operatorControllerString, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func installOrRemoveImagePullSecret(qConfig *qapi.QliksenseConfig) error {
|
func applyImagePullSecret(qConfig *qapi.QliksenseConfig) error {
|
||||||
if pullDockerConfigJsonSecret, err := qConfig.GetPullDockerConfigJsonSecret(); err == nil {
|
if pullDockerConfigJsonSecret, err := qConfig.GetPullDockerConfigJsonSecret(); err == nil {
|
||||||
if dockerConfigJsonSecretYaml, err := pullDockerConfigJsonSecret.ToYaml(""); err != nil {
|
if dockerConfigJsonSecretYaml, err := pullDockerConfigJsonSecret.ToYaml(""); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err := qapi.KubectlApply(string(dockerConfigJsonSecretYaml), ""); err != nil {
|
} else if err := qapi.KubectlApply(string(dockerConfigJsonSecretYaml), ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
deleteDockerConfigJsonSecret := qapi.DockerConfigJsonSecret{
|
|
||||||
Name: pullSecretName,
|
|
||||||
}
|
|
||||||
if deleteDockerConfigJsonSecretYaml, err := deleteDockerConfigJsonSecret.ToYaml(""); err != nil {
|
|
||||||
return err
|
|
||||||
} else if err := qapi.KubectlDelete(string(deleteDockerConfigJsonSecretYaml), ""); err != nil {
|
|
||||||
qapi.LogDebugMessage("failed deleting %v, error: %v\n", pullSecretName, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -192,7 +227,7 @@ images:
|
|||||||
func (q *Qliksense) applyCR(cr *qapi.QliksenseCR) error {
|
func (q *Qliksense) applyCR(cr *qapi.QliksenseCR) error {
|
||||||
// install operator cr into cluster
|
// install operator cr into cluster
|
||||||
//get the current context cr
|
//get the current context cr
|
||||||
fmt.Println("Install operator CR into cluster")
|
fmt.Println("Installing operator CR into the cluster")
|
||||||
r, err := cr.GetString()
|
r, err := cr.GetString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -204,7 +239,7 @@ func (q *Qliksense) applyCR(cr *qapi.QliksenseCR) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Qliksense) createK8sResoruceBeforePatch(qcr *qapi.QliksenseCR) error {
|
func (q *Qliksense) createK8sResourceBeforePatch(qcr *qapi.QliksenseCR) error {
|
||||||
for svc, nvs := range qcr.Spec.Secrets {
|
for svc, nvs := range qcr.Spec.Secrets {
|
||||||
for _, nv := range nvs {
|
for _, nv := range nvs {
|
||||||
if isK8sSecretNeedToCreate(nv) {
|
if isK8sSecretNeedToCreate(nv) {
|
||||||
@@ -223,3 +258,26 @@ func (q *Qliksense) createK8sResoruceBeforePatch(qcr *qapi.QliksenseCR) error {
|
|||||||
func isK8sSecretNeedToCreate(nv config.NameValue) bool {
|
func isK8sSecretNeedToCreate(nv config.NameValue) bool {
|
||||||
return nv.ValueFrom != nil
|
return nv.ValueFrom != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func enforceEula() {
|
||||||
|
fmt.Println(eulaText)
|
||||||
|
fmt.Print(eulaPrompt)
|
||||||
|
answer := readAnswerFromTty()
|
||||||
|
if strings.ToLower(answer) != "y" {
|
||||||
|
fmt.Println(eulaErrorInstruction)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAnswerFromTty() string {
|
||||||
|
t, err := tty.Open()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer t.Close()
|
||||||
|
answer, err := t.ReadString()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return answer
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,20 +5,18 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gobuffalo/packr/v2"
|
||||||
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||||
"sigs.k8s.io/kustomize/api/resid"
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
"sigs.k8s.io/kustomize/api/resource"
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
|
||||||
"github.com/gobuffalo/packr/v2"
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateK8sResoruceBeforePatch(t *testing.T) {
|
func TestCreateK8sResourceBeforePatch(t *testing.T) {
|
||||||
td := setup()
|
td := setup()
|
||||||
sampleCr := `
|
sampleCr := `
|
||||||
apiVersion: qlik.com/v1
|
apiVersion: qlik.com/v1
|
||||||
@@ -43,20 +41,12 @@ spec:
|
|||||||
value: "yes"
|
value: "yes"
|
||||||
secrets:
|
secrets:
|
||||||
qliksense:
|
qliksense:
|
||||||
- name: mongoDbUri
|
- name: mongodbUri
|
||||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||||
profile: docker-desktop
|
profile: docker-desktop`
|
||||||
rotateKeys: "yes"`
|
|
||||||
|
|
||||||
crFile := filepath.Join(testDir, "install_test.yaml")
|
|
||||||
ioutil.WriteFile(crFile, []byte(sampleCr), 0644)
|
|
||||||
q := New(testDir)
|
q := New(testDir)
|
||||||
file, e := os.Open(crFile)
|
if err := q.LoadCr([]byte(sampleCr), false); err != nil {
|
||||||
if e != nil {
|
|
||||||
t.Log(e)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if err := q.LoadCr(file, false); err != nil {
|
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -66,7 +56,7 @@ spec:
|
|||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
if err = q.createK8sResoruceBeforePatch(cr); err != nil {
|
if err = q.createK8sResourceBeforePatch(cr); err != nil {
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|||||||
21
pkg/qliksense/keys.go
Normal file
21
pkg/qliksense/keys.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package qliksense
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
"github.com/qlik-oss/k-apis/pkg/cr"
|
||||||
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (q *Qliksense) DeleteKeysClusterBackup() error {
|
||||||
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
|
if qcr, err := qConfig.GetCurrentCR(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if userHomeDir, err := homedir.Dir(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if err := cr.DeleteKeysClusterBackup(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/api/resource"
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"github.com/Shopify/ejson"
|
"github.com/Shopify/ejson"
|
||||||
"github.com/qlik-oss/k-apis/pkg/config"
|
"github.com/qlik-oss/k-apis/pkg/config"
|
||||||
@@ -276,9 +276,11 @@ func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
|
|||||||
configPath := filepath.Join(tmpDir, "config")
|
configPath := filepath.Join(tmpDir, "config")
|
||||||
if repo, err := kapis_git.CloneRepository(configPath, defaultConfigRepoGitUrl, nil); err != nil {
|
if repo, err := kapis_git.CloneRepository(configPath, defaultConfigRepoGitUrl, nil); err != nil {
|
||||||
t.Fatalf("unexpected error: %v\n", err)
|
t.Fatalf("unexpected error: %v\n", err)
|
||||||
} else if err := kapis_git.Checkout(repo, "e38df644e759abf0b5941c1511d1a2cd5e3c42fa", "", nil); err != nil {
|
} else if err := kapis_git.Checkout(repo, "e38df644e759abf0b5941c1511d1a2cd5e3c42fa", "commit-e38df644e759abf0b5941c1511d1a2cd5e3c42fa", nil); err != nil {
|
||||||
t.Fatalf("unexpected error: %v\n", err)
|
t.Fatalf("unexpected error: %v\n", err)
|
||||||
}
|
}
|
||||||
|
//tmpDir := "/var/folders/mf/5hs1qkq508q_scjbhxhmf9qwjrp346/T/679268230"
|
||||||
|
//configPath := "/var/folders/mf/5hs1qkq508q_scjbhxhmf9qwjrp346/T/679268230/config"
|
||||||
|
|
||||||
cr := &config.CRSpec{
|
cr := &config.CRSpec{
|
||||||
ManifestsRoot: configPath,
|
ManifestsRoot: configPath,
|
||||||
@@ -310,8 +312,8 @@ func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if resource["kind"].(string) == "Secret" && strings.Contains(resource["metadata"].(map[string]interface{})["name"].(string), "users-secrets-") {
|
if resource["kind"].(string) == "Secret" && strings.Contains(resource["metadata"].(map[interface{}]interface{})["name"].(string), "users-secrets-") {
|
||||||
keyIdBase64 = resource["data"].(map[string]interface{})["tokenAuthPrivateKeyId"].(string)
|
keyIdBase64 = resource["data"].(map[interface{}]interface{})["tokenAuthPrivateKeyId"].(string)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,13 @@ package qliksense
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (q *Qliksense) LoadCr(reader io.Reader, overwriteExistingContext bool) error {
|
func (q *Qliksense) LoadCr(crBytes []byte, overwriteExistingContext bool) error {
|
||||||
if crBytes, err := ioutil.ReadAll(reader); err != nil {
|
if crName, err := q.loadCrStringIntoFileSystem(string(crBytes), overwriteExistingContext); err != nil {
|
||||||
return err
|
|
||||||
} else if crName, err := q.loadCrStringIntoFileSystem(string(crBytes), overwriteExistingContext); err != nil {
|
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("cr name: [ " + crName + " ] has been loaded")
|
fmt.Println("cr name: [ " + crName + " ] has been loaded")
|
||||||
@@ -21,16 +17,6 @@ func (q *Qliksense) LoadCr(reader io.Reader, overwriteExistingContext bool) erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Qliksense) IsEulaAcceptedInCrFile(reader io.Reader) (bool, error) {
|
|
||||||
if crBytes, err := ioutil.ReadAll(reader); err != nil {
|
|
||||||
return false, err
|
|
||||||
} else if cr, err := qapi.CreateCRObjectFromString(string(crBytes)); err != nil {
|
|
||||||
return false, err
|
|
||||||
} else {
|
|
||||||
return cr.IsEULA(), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Qliksense) loadCrStringIntoFileSystem(crstr string, overwriteExistingContext bool) (string, error) {
|
func (q *Qliksense) loadCrStringIntoFileSystem(crstr string, overwriteExistingContext bool) (string, error) {
|
||||||
cr, err := qapi.CreateCRObjectFromString(crstr)
|
cr, err := qapi.CreateCRObjectFromString(crstr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -69,8 +55,8 @@ func (q *Qliksense) loadCrStringIntoFileSystem(crstr string, overwriteExistingCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cr.Spec.FetchSource != nil && cr.Spec.FetchSource.AccessToken != "" {
|
if cr.Spec.Git != nil && cr.Spec.Git.AccessToken != "" {
|
||||||
if err := cr.SetFetchAccessToken(cr.Spec.FetchSource.AccessToken, encryptionKey); err != nil {
|
if err := cr.SetFetchAccessToken(cr.Spec.Git.AccessToken, encryptionKey); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package qliksense
|
package qliksense
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
@@ -35,10 +32,9 @@ spec:
|
|||||||
value: "yes"
|
value: "yes"
|
||||||
secrets:
|
secrets:
|
||||||
qliksense:
|
qliksense:
|
||||||
- name: mongoDbUri
|
- name: mongodbUri
|
||||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||||
profile: docker-desktop
|
profile: docker-desktop`
|
||||||
rotateKeys: "yes"`
|
|
||||||
sampleCr2 := `
|
sampleCr2 := `
|
||||||
apiVersion: qlik.com/v1
|
apiVersion: qlik.com/v1
|
||||||
kind: Qliksense
|
kind: Qliksense
|
||||||
@@ -62,10 +58,9 @@ spec:
|
|||||||
value: "yes"
|
value: "yes"
|
||||||
secrets:
|
secrets:
|
||||||
qliksense:
|
qliksense:
|
||||||
- name: mongoDbUri
|
- name: mongodbUri
|
||||||
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
||||||
profile: docker-desktop
|
profile: docker-desktop`
|
||||||
rotateKeys: "yes"`
|
|
||||||
|
|
||||||
duplicateCr := `
|
duplicateCr := `
|
||||||
apiVersion: qlik.com/v1
|
apiVersion: qlik.com/v1
|
||||||
@@ -79,30 +74,13 @@ spec:
|
|||||||
repository: https://github.com/ffoysal/qliksense-k8s
|
repository: https://github.com/ffoysal/qliksense-k8s
|
||||||
accessToken: abababababababaab
|
accessToken: abababababababaab
|
||||||
userName: "blblbl"`
|
userName: "blblbl"`
|
||||||
crFile1 := filepath.Join(testDir, "testcr1.yaml")
|
|
||||||
ioutil.WriteFile(crFile1, []byte(sampleCr1), 0644)
|
|
||||||
crFile2 := filepath.Join(testDir, "testcr2.yaml")
|
|
||||||
ioutil.WriteFile(crFile2, []byte(sampleCr2), 0644)
|
|
||||||
|
|
||||||
dupCrFile := filepath.Join(testDir, "dupcr.yaml")
|
|
||||||
ioutil.WriteFile(dupCrFile, []byte(duplicateCr), 0644)
|
|
||||||
|
|
||||||
q := New(testDir)
|
q := New(testDir)
|
||||||
file1, e := os.Open(crFile1)
|
if err := q.LoadCr([]byte(sampleCr1), false); err != nil {
|
||||||
if e != nil {
|
|
||||||
t.Log(e)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if err := q.LoadCr(file1, false); err != nil {
|
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
file2, e := os.Open(crFile2)
|
if err := q.LoadCr([]byte(sampleCr2), false); err != nil {
|
||||||
if e != nil {
|
|
||||||
t.Log(e)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if err := q.LoadCr(file2, false); err != nil {
|
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -128,12 +106,7 @@ spec:
|
|||||||
if qConfig.Spec.CurrentContext != "qlik-test3" {
|
if qConfig.Spec.CurrentContext != "qlik-test3" {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
file, e := os.Open(dupCrFile)
|
if err := q.LoadCr([]byte(duplicateCr), false); err == nil {
|
||||||
if e != nil {
|
|
||||||
t.Log(e)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if err := q.LoadCr(file, false); err == nil {
|
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
td()
|
td()
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func (q *Qliksense) getYamlFromPackrFile(packrFile string) string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Cannot read file %s", packrFile)
|
fmt.Printf("Cannot read file %s", packrFile)
|
||||||
}
|
}
|
||||||
return fmt.Sprintln("#soruce: " + packrFile + "\n\n" + s + "\n---")
|
return fmt.Sprintln("#source: " + packrFile + "\n\n" + s + "\n---")
|
||||||
}
|
}
|
||||||
func (q *Qliksense) getFileList(resourceType string) []string {
|
func (q *Qliksense) getFileList(resourceType string) []string {
|
||||||
var resList []string
|
var resList []string
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (q *Qliksense) UpgradeQK8s(keepPatchFiles bool) error {
|
func (q *Qliksense) UpgradeQK8s(cleanPatchFiles bool) error {
|
||||||
|
|
||||||
// step1: get CR
|
// step1: get CR
|
||||||
// step2: run kustomize
|
// step2: run kustomize
|
||||||
@@ -14,12 +14,10 @@ func (q *Qliksense) UpgradeQK8s(keepPatchFiles bool) error {
|
|||||||
|
|
||||||
// fetch the version
|
// fetch the version
|
||||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
if !keepPatchFiles {
|
if cleanPatchFiles {
|
||||||
defer func() {
|
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
|
||||||
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
|
fmt.Printf("error removing temporary changes to the config: %v\n", err)
|
||||||
fmt.Printf("error removing temporary changes to the config: %v\n", err)
|
}
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qcr, err := qConfig.GetCurrentCR()
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
@@ -27,7 +25,6 @@ func (q *Qliksense) UpgradeQK8s(keepPatchFiles bool) error {
|
|||||||
fmt.Println("cannot get the current-context cr", err)
|
fmt.Println("cannot get the current-context cr", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qcr.Spec.RotateKeys = "no"
|
|
||||||
|
|
||||||
dcr, err := qConfig.GetDecryptedCr(qcr)
|
dcr, err := qConfig.GetDecryptedCr(qcr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user