Compare commits

...

20 Commits

Author SHA1 Message Date
Foysal Iqbal
44b936a9aa add custom crds (#277)
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-04-04 01:16:13 -04:00
Foysal Iqbal
0e6a1ab18d add first version of editor (#275)
* add first version of editor

Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-04-03 01:22:29 -04:00
Foysal Iqbal
60a77dab5c Merge pull request #274 from qlik-oss/preflight-config
Preflight config
2020-04-02 17:30:24 -04:00
Foysal Iqbal
b041d8be3c Merge branch 'master' into preflight-config 2020-04-02 16:15:11 -04:00
Foysal Iqbal
a73209864c add preflight config in a file for pull/push
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-04-02 16:14:46 -04:00
Jacob Martin
a662e26867 update kustomize to qlik/v.0.0.18 (#273) 2020-04-02 14:14:47 -04:00
Foysal Iqbal
198c631bd1 Merge pull request #271 from qlik-oss/fix-pull
fix pull
2020-04-02 12:10:24 -04:00
Foysal Iqbal
6c38708c9f Merge pull request #272 from qlik-oss/temp-version
change min version
2020-04-02 11:57:02 -04:00
Foysal Iqbal
8c0ffc667d change min version
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-04-02 09:40:09 -04:00
Foysal Iqbal
b8fc1474f8 fix pull
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-04-02 01:04:03 -04:00
Foysal Iqbal
bcb0c44300 fix pull
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-04-02 01:01:50 -04:00
Foysal Iqbal
e2294e48c4 fix pull
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-04-02 00:58:23 -04:00
Foysal Iqbal
ad7861cd13 Merge pull request #270 from qlik-oss/prepare-preflight
prepare for pre-flight
2020-04-01 20:39:19 -04:00
Foysal Iqbal
f17e27f2ef prepare for pre-flight
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-04-01 20:14:31 -04:00
Foysal Iqbal
77bf52e0b0 prepare for pre-flight
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-04-01 20:12:09 -04:00
Ashwathi Shiva
3819f29412 Preflight service, pod and deployment (#268)
* Added preflight deployment, service and pod checks and updated readme
2020-04-01 16:08:49 -04:00
Andriy Bulynko
bdbcc665ae Upgrading qlik-oss/kustomize to version qlik/v0.0.17 (#267) 2020-04-01 12:15:36 -04:00
Ashwathi Shiva
b3d0eff376 troubleshoot-preflight cleaned up and reworked (#263)
* switched to client-go approach to doing preflight-checks, dns check, k8s-version check added, updated readme
2020-03-31 17:07:08 -04:00
Foysal Iqbal
070abea0d8 Merge pull request #265 from qlik-oss/fix-flag-all
fix flag all
2020-03-31 09:44:37 -04:00
Foysal Iqbal
d04defdf13 fix flag all
Signed-off-by: Foysal Iqbal <mqb@qlik.com>
2020-03-31 09:35:32 -04:00
27 changed files with 1457 additions and 490 deletions

2
.gitignore vendored
View File

@@ -7,3 +7,5 @@ pkg/qliksense/qliksense-packr.go
pkg/qliksense/docker-registry
/pkg/qliksense/tests
.DS_Store
.idea/

View File

@@ -42,3 +42,20 @@ func configViewCmd(q *qliksense.Qliksense) *cobra.Command {
}
return c
}
func configEditCmd(q *qliksense.Qliksense) *cobra.Command {
c := &cobra.Command{
Use: "edit [context-name]",
Short: "Edit the context cr",
Long: `edit the context cr. if no context name provided default context will be edited
It will open the vim editor unless KUBE_EDITOR is defined`,
Example: `qliksense config edit [context-name]`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 1 {
return q.EditCR(args[0])
}
return q.EditCR("")
},
}
return c
}

View File

@@ -37,6 +37,6 @@ func crdsInstallCmd(q *qliksense.Qliksense) *cobra.Command {
},
}
f := c.Flags()
f.BoolVarP(&opts.All, "all", "a", false, "Include All CRDs")
f.BoolVarP(&opts.All, "all", "", false, "Include All CRDs")
return c
}

View File

@@ -20,7 +20,7 @@ func preflightCmd(q *qliksense.Qliksense) *cobra.Command {
return preflightCmd
}
func preflightCheckDnsCmd(q *qliksense.Qliksense) *cobra.Command {
func pfDnsCheckCmd(q *qliksense.Qliksense) *cobra.Command {
var preflightDnsCmd = &cobra.Command{
Use: "dns",
Short: "perform preflight dns check",
@@ -28,19 +28,27 @@ func preflightCheckDnsCmd(q *qliksense.Qliksense) *cobra.Command {
Example: `qliksense preflight dns`,
RunE: func(cmd *cobra.Command, args []string) error {
qp := &preflight.QliksensePreflight{Q: q}
err := qp.DownloadPreflight()
// Preflight DNS check
fmt.Printf("Preflight DNS check\n")
fmt.Println("---------------------")
namespace, kubeConfigContents, err := preflight.InitPreflight()
if err != nil {
err = fmt.Errorf("There has been an error downloading preflight: %+v", err)
log.Println(err)
return err
fmt.Printf("Preflight DNS check FAILED\n")
log.Fatal(err)
}
return qp.CheckDns()
if err = qp.CheckDns(namespace, kubeConfigContents); err != nil {
fmt.Println(err)
fmt.Print("Preflight DNS check FAILED\n")
log.Fatal()
}
return nil
},
}
return preflightDnsCmd
}
func preflightCheckK8sVersionCmd(q *qliksense.Qliksense) *cobra.Command {
func pfK8sVersionCheckCmd(q *qliksense.Qliksense) *cobra.Command {
var preflightCheckK8sVersionCmd = &cobra.Command{
Use: "k8s-version",
Short: "check k8s version",
@@ -48,19 +56,27 @@ func preflightCheckK8sVersionCmd(q *qliksense.Qliksense) *cobra.Command {
Example: `qliksense preflight k8s-version`,
RunE: func(cmd *cobra.Command, args []string) error {
qp := &preflight.QliksensePreflight{Q: q}
err := qp.DownloadPreflight()
// Preflight Kubernetes minimum version check
fmt.Printf("Preflight kubernetes minimum version check\n")
fmt.Println("------------------------------------------")
namespace, kubeConfigContents, err := preflight.InitPreflight()
if err != nil {
err = fmt.Errorf("There has been an error downloading preflight: %+v", err)
log.Println(err)
return err
fmt.Printf("Preflight kubernetes minimum version check FAILED\n")
log.Fatal(err)
}
return qp.CheckK8sVersion()
if err = qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
fmt.Println(err)
fmt.Printf("Preflight kubernetes minimum version check FAILED\n")
log.Fatal()
}
return nil
},
}
return preflightCheckK8sVersionCmd
}
func preflightAllChecksCmd(q *qliksense.Qliksense) *cobra.Command {
func pfAllChecksCmd(q *qliksense.Qliksense) *cobra.Command {
var preflightAllChecksCmd = &cobra.Command{
Use: "all",
Short: "perform all checks",
@@ -68,14 +84,137 @@ func preflightAllChecksCmd(q *qliksense.Qliksense) *cobra.Command {
Example: `qliksense preflight all`,
RunE: func(cmd *cobra.Command, args []string) error {
qp := &preflight.QliksensePreflight{Q: q}
err := qp.DownloadPreflight()
// Preflight run all checks
fmt.Printf("Running all preflight checks\n")
namespace, kubeConfigContents, err := preflight.InitPreflight()
if err != nil {
err = fmt.Errorf("There has been an error downloading preflight: %+v", err)
log.Println(err)
return err
fmt.Println(err)
fmt.Printf("Running preflight check suite has FAILED...\n")
log.Fatal()
}
return qp.RunAllPreflightChecks()
qp.RunAllPreflightChecks(namespace, kubeConfigContents)
return nil
},
}
return preflightAllChecksCmd
}
func pfDeploymentCheckCmd(q *qliksense.Qliksense) *cobra.Command {
var pfDeploymentCheckCmd = &cobra.Command{
Use: "deployment",
Short: "perform preflight deploymwnt check",
Long: `perform preflight deployment check to ensure that we can create deployments in the cluster`,
Example: `qliksense preflight deployment`,
RunE: func(cmd *cobra.Command, args []string) error {
qp := &preflight.QliksensePreflight{Q: q}
// Preflight deployments check
fmt.Printf("Preflight deployment check\n")
fmt.Println("--------------------------")
namespace, kubeConfigContents, err := preflight.InitPreflight()
if err != nil {
fmt.Printf("Preflight deployment check FAILED\n")
log.Fatal(err)
}
if err = qp.CheckDeployment(namespace, kubeConfigContents); err != nil {
fmt.Println(err)
fmt.Print("Preflight deploy check FAILED\n")
log.Fatal()
}
return nil
},
}
return pfDeploymentCheckCmd
}
func pfServiceCheckCmd(q *qliksense.Qliksense) *cobra.Command {
var pfServiceCheckCmd = &cobra.Command{
Use: "service",
Short: "perform preflight service check",
Long: `perform preflight service check to ensure that we are able to create services in the cluster`,
Example: `qliksense preflight service`,
RunE: func(cmd *cobra.Command, args []string) error {
qp := &preflight.QliksensePreflight{Q: q}
// Preflight service check
fmt.Printf("Preflight service check\n")
fmt.Println("-----------------------")
namespace, kubeConfigContents, err := preflight.InitPreflight()
if err != nil {
fmt.Printf("Preflight service check FAILED\n")
log.Fatal(err)
}
if err = qp.CheckService(namespace, kubeConfigContents); err != nil {
fmt.Println(err)
fmt.Print("Preflight service check FAILED\n")
log.Fatal()
}
return nil
},
}
return pfServiceCheckCmd
}
func pfPodCheckCmd(q *qliksense.Qliksense) *cobra.Command {
var pfPodCheckCmd = &cobra.Command{
Use: "pod",
Short: "perform preflight pod check",
Long: `perform preflight pod check to ensure we can create pods in the cluster`,
Example: `qliksense preflight pod`,
RunE: func(cmd *cobra.Command, args []string) error {
qp := &preflight.QliksensePreflight{Q: q}
// Preflight pod check
fmt.Printf("Preflight pod check\n")
fmt.Println("--------------------")
namespace, kubeConfigContents, err := preflight.InitPreflight()
if err != nil {
fmt.Printf("Preflight pod check FAILED\n")
log.Fatal(err)
}
if err = qp.CheckPod(namespace, kubeConfigContents); err != nil {
fmt.Println(err)
fmt.Print("Preflight pod check FAILED\n")
log.Fatal()
}
return nil
},
}
return pfPodCheckCmd
}
func pfCreateRoleCheckCmd(q *qliksense.Qliksense) *cobra.Command {
var preflightDnsCmd = &cobra.Command{
Use: "create-role",
Short: "preflight create role check",
Long: `perform preflight role check to ensure we are able to create a role in the cluster`,
Example: `qliksense preflight create-role`,
RunE: func(cmd *cobra.Command, args []string) error {
qp := &preflight.QliksensePreflight{Q: q}
// Preflight create-role check
fmt.Printf("Preflight create-role check\n")
fmt.Println("---------------------------")
namespace, kubeConfigContents, err := preflight.InitPreflight()
if err != nil {
fmt.Printf("Preflight create-role check FAILED\n")
log.Fatal(err)
}
if err = qp.CreateRoleCheck(namespace, kubeConfigContents); err != nil {
fmt.Println(err)
fmt.Print("Preflight role-check FAILED\n")
log.Fatal()
}
return nil
},
}
return preflightDnsCmd
}
// preflightCmd.AddCommand(pfMongoCheckCmd(p))
// preflightCmd.AddCommand(pfServiceCheckCmd(p))
// preflightCmd.AddCommand(pfCreateRoleBindingCheckCmd(p))
// preflightCmd.AddCommand(pfCreateServiceAccountCheckCmd(p))
// preflightCmd.AddCommand(pfCreateRBCheckCmd(p))

View File

@@ -20,28 +20,7 @@ func pullQliksenseImages(q *qliksense.Qliksense) *cobra.Command {
if err != nil {
return err
}
qConfig := qapi.NewQConfig(q.QliksenseHome)
if version == "" {
if qcr, err := qConfig.GetCurrentCR(); err != nil {
return err
} else {
version = qcr.GetLabelFromCr("version")
}
}
if version != "" {
if !qConfig.IsRepoExistForCurrent(version) {
if err := q.FetchQK8s(version); err != nil {
return err
}
}
if err := qConfig.SwitchCurrentCRToVersionAndProfile(version, opts.Profile); err != nil {
return err
}
}
return q.PullImagesForCurrentCR()
return q.PullImages(version, opts.Profile)
},
}
f := cmd.Flags()

View File

@@ -12,6 +12,7 @@ import (
"github.com/mitchellh/go-homedir"
"github.com/qlik-oss/sense-installer/pkg"
"github.com/qlik-oss/sense-installer/pkg/api"
"github.com/qlik-oss/sense-installer/pkg/preflight"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@@ -100,6 +101,10 @@ func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
if err := p.SetUpQliksenseDefaultContext(); err != nil {
panic(err)
}
pf := preflight.NewPreflightConfig(p.QliksenseHome)
if err := pf.Initialize(); err != nil {
panic(err)
}
globalEulaPostRun(cmd, p)
}
},
@@ -185,6 +190,8 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
// add clean-config-repo-patches command as a sub-command to the app config sub-command
configCmd.AddCommand(cleanConfigRepoPatchesCmd(p))
// open editor for config
configCmd.AddCommand(configEditCmd(p))
// add uninstall command
cmd.AddCommand(uninstallCmd(p))
@@ -195,11 +202,17 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
// add preflight command
preflightCmd := preflightCmd(p)
preflightCmd.AddCommand(preflightCheckDnsCmd(p))
preflightCmd.AddCommand(preflightCheckK8sVersionCmd(p))
preflightCmd.AddCommand(preflightAllChecksCmd(p))
//preflightCmd.AddCommand(preflightCheckMongoCmd(p))
//preflightCmd.AddCommand(preflightCheckAllCmd(p))
preflightCmd.AddCommand(pfDnsCheckCmd(p))
preflightCmd.AddCommand(pfK8sVersionCheckCmd(p))
preflightCmd.AddCommand(pfAllChecksCmd(p))
// preflightCmd.AddCommand(pfMongoCheckCmd(p))
preflightCmd.AddCommand(pfDeploymentCheckCmd(p))
preflightCmd.AddCommand(pfServiceCheckCmd(p))
preflightCmd.AddCommand(pfPodCheckCmd(p))
preflightCmd.AddCommand(pfCreateRoleCheckCmd(p))
// preflightCmd.AddCommand(pfCreateRoleBindingCheckCmd(p))
// preflightCmd.AddCommand(pfCreateServiceAccountCheckCmd(p))
// preflightCmd.AddCommand(pfCreateRBCheckCmd(p))
cmd.AddCommand(preflightCmd)
cmd.AddCommand(loadCrFile(p))

View File

@@ -29,19 +29,20 @@ The expected output should be similar to the one shown below.
```console
$ qliksense preflight dns
Creating resources to run preflight checks
deployment.apps/qnginx001 created
service/qnginx001 created
pod/qnginx001-6db5fc95c5-s9sl2 condition met
Running Preflight checks ⠇
--- PASS DNS check
--- DNS check passed
--- PASS cluster-preflight-checks
PASS
DNS check completed, cleaning up resources now
service "qnginx001" deleted
deployment.extensions "qnginx001" deleted
Preflight DNS check
---------------------
Created deployment "dep-dns-preflight-check"
Created service "svc-dns-pf-check"
Created pod: pf-pod-1
Fetching pod: pf-pod-1
Fetching pod: pf-pod-1
Exec-ing into the container...
Preflight DNS check: PASSED
Completed preflight DNS check
Cleaning up resources...
Deleted pod: pf-pod-1
Deleted service: svc-dns-pf-check
Deleted deployment: dep-dns-preflight-check
```
@@ -51,16 +52,61 @@ The command to run this check and the expected similar output are as shown below
```console
$ qliksense preflight k8s-version
Minimum Kubernetes version supported: 1.11.0
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-13T18:08:14Z", GoVersion:"go1.13.8", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:07:57Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
Running Preflight checks ⠇
--- PASS Required Kubernetes Version
--- Good to go.
--- PASS cluster-preflight-checks
PASS
Preflight kubernetes minimum version check
------------------------------------------
Kubernetes API Server version: v1.15.5
Current K8s Version: 1.15.5
Current 1.15.5 is greater than minimum required version:1.11.0, hence good to go
Preflight minimum kubernetes version check: PASSED
Completed Preflight kubernetes minimum version check
Minimum kubernetes version check completed
```
### Service check
We use the commmand below to test if we are able to create a service in the cluster.
```console
$ qliksense preflight service
Preflight service check
-----------------------
Preflight service check:
Created service "svc-pf-check"
Preflight service creation check: PASSED
Cleaning up resources...
Deleted service: svc-pf-check
Completed preflight service check
```
### Deployment check
We use the commmand below to test if we are able to create a deployment in the cluster. After the test exexutes, we wait until the created deployment terminates before we exit the command.
```console
$ qliksense preflight deployment
Preflight deployment check
-----------------------
Preflight deployment check:
Created deployment "deployment-preflight-check"
Preflight Deployment check: PASSED
Cleaning up resources...
Deleted deployment: deployment-preflight-check
Completed preflight deployment check
```
### Pod check
We use the commmand below to test if we are able to create a pod in the cluster.
```console
$ qliksense preflight pod
Preflight pod check
--------------------
Preflight pod check:
Created pod: pod-pf-check
Preflight pod creation check: PASSED
Cleaning up resources...
Deleted pod: pod-pf-check
Completed preflight pod check
```
### Running all checks
@@ -70,31 +116,31 @@ $ qliksense preflight all
Running all preflight checks
Running DNS check...
Creating resources to run preflight checks
deployment.apps/qnginx001 created
service/qnginx001 created
pod/qnginx001-6db5fc95c5-grwv2 condition met
Running Preflight checks ⠇
--- PASS DNS check
--- DNS check passed
--- PASS cluster-preflight-checks
PASS
Preflight DNS check
-------------------
Created deployment "dep-dns-preflight-check"
Created service "svc-dns-pf-check"
Created pod: pf-pod-1
Fetching pod: pf-pod-1
Fetching pod: pf-pod-1
Exec-ing into the container...
Preflight DNS check: PASSED
Completed preflight DNS check
Cleaning up resources...
Deleted pod: pf-pod-1
Deleted service: svc-dns-pf-check
Deleted deployment: dep-dns-preflight-check
DNS check completed, cleaning up resources now
service "qnginx001" deleted
deployment.extensions "qnginx001" deleted
Running minimum kubernetes version check...
Minimum Kubernetes version supported: 1.11.0
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-13T18:08:14Z", GoVersion:"go1.13.8", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:07:57Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
Running Preflight checks ⠧
--- PASS Required Kubernetes Version
--- Good to go.
--- PASS cluster-preflight-checks
PASS
Minimum kubernetes version check completed
Preflight kubernetes minimum version check
------------------------------------------
Kubernetes API Server version: v1.15.5
Current K8s Version: 1.15.5
Current 1.15.5 is greater than minimum required version:1.11.0, hence good to go
Preflight minimum kubernetes version check: PASSED
Completed Preflight kubernetes minimum version check
...
...
All preflight checks have PASSED
Completed running all preflight checks
```

6
go.mod
View File

@@ -10,12 +10,13 @@ replace (
k8s.io/client-go => k8s.io/client-go v0.0.0-20191016111102-bec269661e48
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200206224201-2e697eccbad9
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200402170547-2e8140160c36
)
require (
cloud.google.com/go v0.52.0 // indirect
cloud.google.com/go/storage v1.5.0 // indirect
github.com/Masterminds/semver/v3 v3.0.3
github.com/Shopify/ejson v1.2.1
github.com/aws/aws-sdk-go v1.28.9 // indirect
github.com/bugsnag/bugsnag-go v1.5.3 // indirect
@@ -41,7 +42,7 @@ require (
github.com/mitchellh/go-homedir v1.1.0
github.com/morikuni/aec v1.0.0 // indirect
github.com/pkg/errors v0.8.1
github.com/qlik-oss/k-apis v0.0.25
github.com/qlik-oss/k-apis v0.0.34
github.com/robfig/cron/v3 v3.0.1
github.com/rogpeppe/go-internal v1.5.2 // indirect
github.com/spf13/cobra v0.0.6
@@ -59,6 +60,7 @@ require (
k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.0
k8s.io/client-go v11.0.0+incompatible
k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51
sigs.k8s.io/kustomize/api v0.3.2
sigs.k8s.io/yaml v1.1.0
)

24
go.sum
View File

@@ -131,6 +131,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
@@ -561,6 +563,8 @@ github.com/hashicorp/go-retryablehttp v0.6.3/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8=
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
@@ -568,6 +572,7 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
@@ -847,16 +852,12 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/qlik-oss/k-apis v0.0.22 h1:tntQEeRqDYkBi2Ku5+xt7ABGMeFPck7+DOKrHUnpzwI=
github.com/qlik-oss/k-apis v0.0.22/go.mod h1:DNiWYqCqPIN216l7+1rccArNIYPb1Le7kYDcPSyNp+Q=
github.com/qlik-oss/k-apis v0.0.23 h1:w4lj2PHDTtKkukgoT2a6/jAY3NkkI/V0vNetkRzEXXY=
github.com/qlik-oss/k-apis v0.0.23/go.mod h1:DNiWYqCqPIN216l7+1rccArNIYPb1Le7kYDcPSyNp+Q=
github.com/qlik-oss/k-apis v0.0.24 h1:cBXggOMeEaUpxO91TfI2jQToh5r/ojBPcjuJM7cBRIM=
github.com/qlik-oss/k-apis v0.0.24/go.mod h1:DNiWYqCqPIN216l7+1rccArNIYPb1Le7kYDcPSyNp+Q=
github.com/qlik-oss/k-apis v0.0.25 h1:OF4YZDklkNyvPKKLOQ4a8JMQaFaqD/vevl6wOOzlhek=
github.com/qlik-oss/k-apis v0.0.25/go.mod h1:DNiWYqCqPIN216l7+1rccArNIYPb1Le7kYDcPSyNp+Q=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200206224201-2e697eccbad9 h1:iqeqTS4zjp6rPEaxmFB7pemA2CMjOEN5dYSXZaQ82uw=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200206224201-2e697eccbad9/go.mod h1:OCt7FTrRVHj4kmR2xLJJUIqu00BTr6GeF09hSmM17Kw=
github.com/qlik-oss/k-apis v0.0.34 h1:lOC21wz/nNZNmSfTXZSJCOm1BulaZfdg7tAuYb7knAE=
github.com/qlik-oss/k-apis v0.0.34/go.mod h1:DNiWYqCqPIN216l7+1rccArNIYPb1Le7kYDcPSyNp+Q=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200401055330-fa528324112a h1:Vzod5XB+e25ENy5Lse0pXNmSYSDFxSEYhH/6Sj7twPg=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200401055330-fa528324112a/go.mod h1:tSQaDZ4Jt9KwYvD7LlMUPi5nkiGOno3PAKl5/XqEfxs=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200402170547-2e8140160c36 h1:BuT+cnXPQ6mcOWTDS1S8GXy65LAEMdPuNQCC36rMq28=
github.com/qlik-oss/kustomize/api v0.3.3-0.20200402170547-2e8140160c36/go.mod h1:tSQaDZ4Jt9KwYvD7LlMUPi5nkiGOno3PAKl5/XqEfxs=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
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=
@@ -967,6 +968,7 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
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/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
@@ -1001,6 +1003,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
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/go.mod h1:sbmqxXjyLunH1PkF3n7zSlnVeMvmYUuIl9ZVs/7NyCc=
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 h1:p7OofyZ509h8DmPLh8Hn+EIIZm/xYhdZHJ9GnXHdr6U=
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.6 h1:qMJQYPNdtJ7UNYHjX38KXZtltKTqimMuoQjNnSVIuJg=

View File

@@ -133,6 +133,17 @@ func (qc *QliksenseConfig) GetCRFilePath(contextName string) string {
}
return crFilePath
}
func (cr *QliksenseCR) IsRepoExist() bool {
if cr.Spec.ManifestsRoot == "" {
return false
}
if _, err := os.Lstat(cr.Spec.ManifestsRoot); err != nil {
return false
}
return true
}
func (qc *QliksenseConfig) IsRepoExist(contextName, version string) bool {
if _, err := os.Lstat(qc.BuildRepoPathForContext(contextName, version)); err != nil {
return false
@@ -442,6 +453,18 @@ func (cr *QliksenseCR) SetEULA(value string) {
cr.Spec.AddToConfigs("qliksense", "acceptEULA", value)
}
// GetCustomCrdsPath get crds path if exist in the profile dir
func (cr *QliksenseCR) GetCustomCrdsPath() string {
if cr.Spec.ManifestsRoot == "" || cr.Spec.Profile == "" {
return ""
}
crdsPath := filepath.Join(cr.Spec.GetManifestsRoot(), "manifests", cr.Spec.Profile, "crds")
if _, err := os.Lstat(crdsPath); err != nil {
return ""
}
return crdsPath
}
// GetDecryptedCr it decrypts all the encrypted value and return a new CR
func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error) {
newCr := &QliksenseCR{}

View File

@@ -4,13 +4,58 @@ import (
"fmt"
)
func (qp *QliksensePreflight) RunAllPreflightChecks() error {
//run all preflight checks
fmt.Println("Running all preflight checks")
fmt.Printf("\nRunning DNS check...\n")
qp.CheckDns()
fmt.Printf("\nRunning minimum kubernetes version check...\n")
qp.CheckK8sVersion()
func (qp *QliksensePreflight) RunAllPreflightChecks(namespace string, kubeConfigContents []byte) {
checkCount := 0
// Preflight minimum kuberenetes version check
fmt.Printf("\nPreflight kubernetes minimum version check\n")
fmt.Println("------------------------------------------")
if err := qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
fmt.Printf("Preflight kubernetes minimum version check: FAILED\n")
} else {
checkCount++
}
// Preflight deployment check
fmt.Printf("\nPreflight deployment check\n")
fmt.Println("-----------------------")
if err := qp.CheckDeployment(namespace, kubeConfigContents); err != nil {
fmt.Printf("Preflight deployment check: FAILED\n")
} else {
checkCount++
}
// Preflight service check
fmt.Printf("\nPreflight service check\n")
fmt.Println("-----------------------")
if err := qp.CheckService(namespace, kubeConfigContents); err != nil {
fmt.Printf("Preflight service check: FAILED\n")
} else {
checkCount++
}
// Preflight pod check
fmt.Printf("\nPreflight pod check\n")
fmt.Println("-----------------------")
if err := qp.CheckPod(namespace, kubeConfigContents); err != nil {
fmt.Printf("Preflight pod check: FAILED\n")
} else {
checkCount++
}
// Preflight DNS check
fmt.Printf("\nPreflight DNS check\n")
fmt.Println("-------------------")
if err := qp.CheckDns(namespace, kubeConfigContents); err != nil {
fmt.Printf("Preflight DNS check: FAILED\n")
} else {
checkCount++
}
if checkCount == 5 {
fmt.Printf("\nAll preflight checks have PASSED\n")
} else {
fmt.Printf("\n1 or more preflight checks have FAILED\n")
}
fmt.Println("Completed running all preflight checks")
return nil
}

View File

@@ -0,0 +1,119 @@
package preflight
import (
"fmt"
"k8s.io/client-go/kubernetes"
)
func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigContents []byte) error {
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
if err != nil {
err = fmt.Errorf("Kube config error: %v\n", err)
fmt.Print(err)
return err
}
// Deployment check
fmt.Printf("Preflight deployment check: \n")
err = qp.checkPfDeployment(clientset, namespace, "deployment-preflight-check")
if err != nil {
fmt.Println("Preflight Deployment check: FAILED")
return err
}
fmt.Println("Completed preflight deployment check")
return nil
}
func (qp *QliksensePreflight) CheckService(namespace string, kubeConfigContents []byte) error {
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
if err != nil {
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
fmt.Println(err)
return err
}
// Service check
fmt.Printf("\nPreflight service check: \n")
err = checkPfService(clientset, namespace)
if err != nil {
fmt.Println("Preflight Service check: FAILED")
return err
}
fmt.Println("Completed preflight service check")
return nil
}
func (qp *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []byte) error {
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
if err != nil {
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
fmt.Print(err)
return err
}
// Pod check
fmt.Printf("\nPreflight pod check: \n")
err = qp.checkPfPod(clientset, namespace)
if err != nil {
fmt.Println("Preflight Pod check: FAILED")
return err
}
fmt.Println("Completed preflight pod check")
return nil
}
func (qp *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namespace string) error {
// create a pod
podName := "pod-pf-check"
pod, err := createPreflightTestPod(clientset, namespace, podName, qp.GetPreflightConfigObj().GetImageName(nginx))
if err != nil {
err = fmt.Errorf("error: unable to create pod %s - %v\n", podName, err)
return err
}
defer deletePod(clientset, namespace, podName)
if err := waitForPod(clientset, namespace, pod); err != nil {
return err
}
fmt.Println("Preflight pod creation check: PASSED")
fmt.Println("Cleaning up resources...")
return nil
}
func checkPfService(clientset *kubernetes.Clientset, namespace string) error {
// creating service
serviceName := "svc-pf-check"
pfService, err := createPreflightTestService(clientset, namespace, serviceName)
if err != nil {
err = fmt.Errorf("error: unable to create service : %s\n", serviceName)
return err
}
defer deleteService(clientset, namespace, serviceName)
_, err = getService(clientset, namespace, pfService.GetName())
if err != nil {
err = fmt.Errorf("error: unable to retrieve service: %s\n", serviceName)
return err
}
fmt.Println("Preflight service creation check: PASSED")
fmt.Println("Cleaning up resources...")
return nil
}
func (qp *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset, namespace, depName string) error {
// check if we are able to create a deployment
// depName :=
pfDeployment, err := createPreflightTestDeployment(clientset, namespace, depName, qp.GetPreflightConfigObj().GetImageName(nginx))
if err != nil {
err = fmt.Errorf("error: unable to create deployment: %v\n", err)
return err
}
defer deleteDeployment(clientset, namespace, depName)
if err := waitForDeployment(clientset, namespace, pfDeployment); err != nil {
return err
}
fmt.Println("Preflight Deployment check: PASSED")
fmt.Println("Cleaning up resources...")
return nil
}

View File

@@ -1,130 +1,81 @@
package preflight
import (
"bytes"
"fmt"
"html/template"
"io/ioutil"
"path/filepath"
"strings"
"github.com/qlik-oss/sense-installer/pkg/api"
)
const dnsCheckYAML = `
apiVersion: troubleshoot.replicated.com/v1beta1
kind: Preflight
metadata:
name: cluster-preflight-checks
namespace: {{ .namespace }}
spec:
collectors:
- run:
collectorName: spin-up-pod
args: ["-z", "-v", "-w 1", "{{ .serviceName }}", "80"]
command: ["nc"]
image: subfuzion/netcat:latest
imagePullPolicy: IfNotPresent
name: spin-up-pod-check-dns
namespace: {{ .namespace }}
timeout: 30s
const (
nginx = "nginx"
netcat = "netcat"
)
analyzers:
- textAnalyze:
checkName: DNS check
collectorName: spin-up-pod-check-dns
fileName: spin-up-pod.log
regex: succeeded
outcomes:
- fail:
message: DNS check failed
- pass:
message: DNS check passed
`
func (qp *QliksensePreflight) CheckDns() error {
// retrieve namespace
namespace := api.GetKubectlNamespace()
api.LogDebugMessage("Namespace: %s\n", namespace)
tmpl, err := template.New("dnsCheckYAML").Parse(dnsCheckYAML)
if err != nil {
fmt.Printf("cannot parse template: %v", err)
return err
}
tempYaml, err := ioutil.TempFile("", "")
if err != nil {
fmt.Printf("cannot create file: %v", err)
return err
}
api.LogDebugMessage("Temp Yaml file: %s\n", tempYaml.Name())
appName := "qnginx001"
const PreflightChecksDirName = "preflight_checks"
b := bytes.Buffer{}
err = tmpl.Execute(&b, map[string]string{
"namespace": namespace,
"serviceName": appName,
})
func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byte) error {
clientset, clientConfig, err := getK8SClientSet(kubeConfigContents, "")
if err != nil {
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
fmt.Println(err)
return err
}
tempYaml.WriteString(b.String())
// creating Kubectl resources
fmt.Println("Creating resources to run preflight checks")
// kubectl create deployment
opr := fmt.Sprintf("create deployment %s --image=nginx", appName)
err = initiateK8sOps(opr, namespace)
// creating deployment
depName := "dep-dns-preflight-check"
dnsDeployment, err := createPreflightTestDeployment(clientset, namespace, depName, qp.GetPreflightConfigObj().GetImageName(nginx))
if err != nil {
err = fmt.Errorf("error: unable to create deployment: %v\n", err)
fmt.Println(err)
return err
}
defer deleteDeployment(clientset, namespace, depName)
if err := waitForDeployment(clientset, namespace, dnsDeployment); err != nil {
return err
}
// creating service
serviceName := "svc-dns-pf-check"
dnsService, err := createPreflightTestService(clientset, namespace, serviceName)
if err != nil {
err = fmt.Errorf("error: unable to create service : %s\n", serviceName)
return err
}
defer deleteService(clientset, namespace, serviceName)
// create a pod
podName := "pf-pod-1"
dnsPod, err := createPreflightTestPod(clientset, namespace, podName, qp.GetPreflightConfigObj().GetImageName(netcat))
if err != nil {
err = fmt.Errorf("error: unable to create pod : %s\n", podName)
return err
}
defer deletePod(clientset, namespace, podName)
if err := waitForPod(clientset, namespace, dnsPod); err != nil {
return err
}
if len(dnsPod.Spec.Containers) == 0 {
err := fmt.Errorf("error: there are no containers in the pod")
fmt.Println(err)
return err
}
api.LogDebugMessage("Exec-ing into the container...")
stdout, stderr, err := executeRemoteCommand(clientset, clientConfig, dnsPod.Name, dnsPod.Spec.Containers[0].Name, namespace, []string{"nc", "-z", "-v", "-w 1", dnsService.Name, "80"})
if err != nil {
err = fmt.Errorf("error: unable to execute dns check in the cluster: %v", err)
fmt.Println(err)
return err
}
defer func() {
// Deleting deployment..
opr = fmt.Sprintf("delete deployment %s", appName)
// we want to delete the k8s resource here, we dont care a lot about an error here
_ = initiateK8sOps(opr, namespace)
api.LogDebugMessage("delete deployment executed")
}()
// create service
opr = fmt.Sprintf("create service clusterip %s --tcp=80:80", appName)
err = initiateK8sOps(opr, namespace)
if err != nil {
fmt.Println(err)
return err
if strings.HasSuffix(stdout, "succeeded!") || strings.HasSuffix(stderr, "succeeded!") {
fmt.Println("Preflight DNS check: PASSED")
} else {
fmt.Println("Preflight DNS check: FAILED")
}
defer func() {
// delete service
opr = fmt.Sprintf("delete service %s", appName)
// we want to delete the k8s resource here, we dont care a lot about an error here
_ = initiateK8sOps(opr, namespace)
api.LogDebugMessage("delete service executed")
}()
fmt.Println("Completed preflight DNS check")
fmt.Println("Cleaning up resources...")
//kubectl -n $namespace wait --for=condition=ready pod -l app=$appName --timeout=120s
opr = fmt.Sprintf("wait --for=condition=ready pod -l app=%s --timeout=120s", appName)
err = initiateK8sOps(opr, namespace)
if err != nil {
fmt.Println(err)
return err
}
api.LogDebugMessage("kubectl wait executed")
// call preflight
preflightCommand := filepath.Join(qp.Q.QliksenseHome, PreflightChecksDirName, preflightFileName)
err = invokePreflight(preflightCommand, tempYaml)
if err != nil {
fmt.Println(err)
return err
}
fmt.Println("DNS check completed, cleaning up resources now")
return nil
}

View File

@@ -0,0 +1,109 @@
package preflight
import (
"os"
"path/filepath"
api "github.com/qlik-oss/sense-installer/pkg/api"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type PreflightConfig struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Spec *PreflightSpec `json:"spec" yaml:"spec"`
QliksenseHomePath string `json:"-" yaml:"-"`
}
type PreflightSpec struct {
MinK8sVersion string `json:"minK8sVersion,omitempty" yaml:"minK8sVersion,omitempty"`
Images map[string]string `json:"images,omitempty" yaml:"images,omitempty"`
}
//NewPreflightConfigEmpty create empty PreflightConfig object
func NewPreflightConfigEmpty(qHome string) *PreflightConfig {
p := &PreflightConfig{
QliksenseHomePath: qHome,
TypeMeta: metav1.TypeMeta{
APIVersion: "config.qlik.com/v1",
Kind: "PreflightConfig",
},
ObjectMeta: metav1.ObjectMeta{
Name: "PreflightConfigMetadata",
},
Spec: &PreflightSpec{},
}
return p
}
//NewPreflightConfig create empty PreflightConfig object if preflit/preflight-config.yaml not exist
func NewPreflightConfig(qHome string) *PreflightConfig {
p := NewPreflightConfigEmpty(qHome)
conFile := p.GetConfigFilePath()
if _, err := os.Lstat(conFile); err != nil {
return p
}
p = &PreflightConfig{}
if err := api.ReadFromFile(p, conFile); err != nil {
return nil
}
return p
}
//GetConfigFilePath return preflight-config.yaml file path
func (p *PreflightConfig) GetConfigFilePath() string {
return filepath.Join(p.QliksenseHomePath, "preflight", "preflight-config.yaml")
}
//Write write PreflightConfig object into the ~/.qliksense/preflight/preflight-config.yaml file
func (p *PreflightConfig) Write() error {
pDir := filepath.Join(p.QliksenseHomePath, "preflight")
if err := os.MkdirAll(pDir, os.ModePerm); err != nil {
return err
}
return api.WriteToFile(p, p.GetConfigFilePath())
}
func (p *PreflightConfig) AddMinK8sV(version string) {
if p.Spec == nil {
p.Spec = &PreflightSpec{}
}
p.Spec.MinK8sVersion = version
}
func (p *PreflightConfig) AddImage(imageFor, imageName string) {
if p.Spec.Images == nil {
p.Spec.Images = make(map[string]string)
}
p.Spec.Images[imageFor] = imageName
}
func (p *PreflightConfig) GetImageName(imageFor string) string {
if p.Spec.Images == nil {
return ""
}
return p.Spec.Images[imageFor]
}
func (p *PreflightConfig) GetMinK8sVersion() string {
return p.Spec.MinK8sVersion
}
func (p *PreflightConfig) IsExistOnDisk() bool {
if _, err := os.Lstat(p.GetConfigFilePath()); err != nil {
return false
}
return true
}
func (p *PreflightConfig) GetImageMap() map[string]string {
return p.Spec.Images
}
func (p *PreflightConfig) Initialize() error {
if p.IsExistOnDisk() {
return nil
}
p.AddMinK8sV("1.15")
p.AddImage("nginx", "nginx")
p.AddImage("netcat", "subfuzion/netcat")
return p.Write()
}

View File

@@ -0,0 +1,41 @@
package preflight
import (
"io/ioutil"
"testing"
api "github.com/qlik-oss/sense-installer/pkg/api"
)
func Test_Initalize(t *testing.T) {
tempDir, err := ioutil.TempDir("", "")
if err != nil {
t.Log(err)
t.FailNow()
}
pf := NewPreflightConfig(tempDir)
if err := pf.Initialize(); err != nil {
t.Log()
t.FailNow()
}
p := &PreflightConfig{
QliksenseHomePath: tempDir,
}
if err := api.ReadFromFile(p, pf.GetConfigFilePath()); err != nil {
t.Log(err)
t.FailNow()
}
if p.GetMinK8sVersion() != "1.15" {
t.Log("expected k8 version: 1.15, but got " + p.GetMinK8sVersion())
t.Fail()
}
p.AddImage("test", "testimage")
if err := p.Write(); err != nil {
t.Log(err)
t.Fail()
}
p2 := NewPreflightConfig(tempDir)
if p2.GetImageName("test") != "testimage" {
t.Log("expected image name: testimage, got: " + p2.GetImageName("test"))
}
}

View File

@@ -3,136 +3,70 @@ package preflight
import (
"bytes"
"fmt"
"log"
"os"
"os/exec"
"io"
"io/ioutil"
"net/url"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
"github.com/mitchellh/go-homedir"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/kubectl/pkg/scheme"
"github.com/pkg/errors"
"github.com/qlik-oss/sense-installer/pkg/api"
"github.com/qlik-oss/sense-installer/pkg/qliksense"
appsv1 "k8s.io/api/apps/v1"
apiv1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
)
var gracePeriod int64 = 0
type QliksensePreflight struct {
Q *qliksense.Qliksense
}
const (
// preflight releases have the same version
preflightRelease = "v0.9.28"
preflightLinuxFile = "preflight_linux_amd64.tar.gz"
preflightMacFile = "preflight_darwin_amd64.tar.gz"
preflightWindowsFile = "preflight_windows_amd64.zip"
PreflightChecksDirName = "preflight_checks"
preflightFileName = "preflight"
)
var preflightBaseURL = fmt.Sprintf("https://github.com/replicatedhq/troubleshoot/releases/download/%s/", preflightRelease)
func (qp *QliksensePreflight) DownloadPreflight() error {
preflightExecutable := "preflight"
if runtime.GOOS == "windows" {
preflightExecutable += ".exe"
}
preflightInstallDir := filepath.Join(qp.Q.QliksenseHome, PreflightChecksDirName)
exists, err := checkInstalled(preflightInstallDir, preflightExecutable)
if err != nil {
err = fmt.Errorf("There has been an error when trying to determine if preflight installer exists")
log.Println(err)
return err
}
if exists {
// preflight exist, no need to download again.
api.LogDebugMessage("Preflight already exists, proceeding to perform checks")
return nil
}
// Create the Preflight-check directory, download and install preflight
if !api.DirExists(preflightInstallDir) {
api.LogDebugMessage("%s does not exist, creating now\n", preflightInstallDir)
if err := os.Mkdir(preflightInstallDir, os.ModePerm); err != nil {
err = fmt.Errorf("Not able to create %s dir: %v", preflightInstallDir, err)
log.Println(err)
return nil
}
}
api.LogDebugMessage("Preflight-checks install Dir: %s exists", preflightInstallDir)
preflightUrl, preflightFile, err := determinePlatformSpecificUrls(runtime.GOOS)
if err != nil {
err = fmt.Errorf("There was an error when trying to determine platform specific paths")
return err
}
// Download Preflight
err = downloadAndExplode(preflightUrl, preflightInstallDir, preflightFile)
if err != nil {
return err
}
fmt.Println("Downloaded Preflight")
return nil
func (qp *QliksensePreflight) GetPreflightConfigObj() *PreflightConfig {
return NewPreflightConfig(qp.Q.QliksenseHome)
}
func checkInstalled(preflightInstallDir, preflightExecutable string) (bool, error) {
installerExists := true
preflightInstaller := filepath.Join(preflightInstallDir, preflightExecutable)
if api.DirExists(preflightInstallDir) {
if !api.FileExists(preflightInstaller) {
installerExists = false
api.LogDebugMessage("Preflight install directory exists, but preflight installer does not exist")
}
} else {
installerExists = false
}
return installerExists, nil
}
func InitPreflight() (string, []byte, error) {
api.LogDebugMessage("Reading .kube/config file...")
func downloadAndExplode(url, installDir, file string) error {
err := api.DownloadFile(url, installDir, file)
homeDir, err := homedir.Dir()
if err != nil {
return err
err = fmt.Errorf("Unable to deduce home dir\n")
return "", nil, err
}
api.LogDebugMessage("Downloaded File: %s", file)
api.LogDebugMessage("Kube config location: %s\n\n", filepath.Join(homeDir, ".kube", "config"))
fileToUntar := filepath.Join(installDir, file)
api.LogDebugMessage("File to explode: %s", file)
err = api.ExplodePackage(installDir, fileToUntar)
kubeConfig := filepath.Join(homeDir, ".kube", "config")
kubeConfigContents, err := ioutil.ReadFile(kubeConfig)
if err != nil {
return err
err = fmt.Errorf("Unable to deduce home dir\n")
return "", nil, err
}
return nil
}
func determinePlatformSpecificUrls(platform string) (string, string, error) {
var preflightUrl, preflightFile string
if runtime.GOARCH != `amd64` {
err := fmt.Errorf("%s architecture is not supported", runtime.GOARCH)
return "", "", err
// retrieve namespace
namespace := api.GetKubectlNamespace()
// if namespace comes back empty, we will run checks in the default namespace
if namespace == "" {
namespace = "default"
}
switch platform {
case "windows":
preflightFile = preflightWindowsFile
case "darwin":
preflightFile = preflightMacFile
case "linux":
preflightFile = preflightLinuxFile
default:
err := fmt.Errorf("Unable to download the preflight executable for the underlying platform\n")
return "", "", err
}
preflightUrl = fmt.Sprintf("%s%s", preflightBaseURL, preflightFile)
return preflightUrl, preflightFile, nil
api.LogDebugMessage("Namespace: %s\n", namespace)
return namespace, kubeConfigContents, nil
}
func initiateK8sOps(opr, namespace string) error {
@@ -145,46 +79,429 @@ func initiateK8sOps(opr, namespace string) error {
return nil
}
func invokePreflight(preflightCommand string, yamlFile *os.File) error {
var arguments []string
func int32Ptr(i int32) *int32 { return &i }
arguments = append(arguments, yamlFile.Name(), "--interactive=false")
cmd := exec.Command(preflightCommand, arguments...)
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)
}
sterrBuffer := &bytes.Buffer{}
cmd.Stdout = sterrBuffer
cmd.Stderr = sterrBuffer
if err := cmd.Run(); err != nil {
return fmt.Errorf("Error when running preflight command: %v\n", err)
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")
fmt.Println(err)
return nil, nil, err
}
} else {
config, err := clientcmd.Load(kubeconfig)
if err != nil {
err = errors.Wrap(err, "Unable to load kubeconfig")
fmt.Println(err)
return nil, nil, err
}
if contextName != "" {
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")
fmt.Println(err)
return nil, nil, err
}
}
ind := strings.Index(sterrBuffer.String(), "---")
output := sterrBuffer.String()
if ind > -1 {
output = fmt.Sprintf("%s\n%s", output[:ind], output[ind:])
clientset, err := kubernetes.NewForConfig(clientConfig)
if err != nil {
err = errors.Wrap(err, "Unable to create clientset")
fmt.Println(err)
return nil, nil, err
}
fmt.Printf("%v\n", output)
return clientset, clientConfig, nil
}
// Maybe good to retain this part in case we need to process the output in future.
// We are going to look for the first occurance of PASS or FAIL from the end
// there are also some space-like deceiving characters which are being hard to get by
func 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,
},
},
},
},
},
},
},
}
//outputArr := strings.Fields(strings.TrimSpace(output))
//trackSuccess := false
//trackPrg := false
// 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, "error: unable to create deployments in the %s namespace", namespace)
fmt.Println(err)
return nil, err
}
fmt.Printf("Created deployment %q\n", result.GetObjectMeta().GetName())
//for i := len(outputArr) - 1; i >= 0; i-- {
// if strings.TrimSpace(outputArr[i]) != "" {
// if outputArr[i] == "PASS" {
// trackSuccess = true
// trackPrg = true
// } else if outputArr[i] == "FAIL" {
// trackPrg = true
// }
// }
// if trackPrg {
// break
// }
//}
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, "error: unable to get deployments in the %s namespace", namespace)
api.LogDebugMessage("%v\n", err)
return nil, err
}
return deployment, nil
}
func deleteDeployment(clientset *kubernetes.Clientset, namespace, name string) error {
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 {
fmt.Println(err)
return err
}
if err := waitForDeploymentToDelete(clientset, namespace, name); err != nil {
return err
}
fmt.Printf("Deleted deployment: %s\n", name)
return nil
}
func 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 {
fmt.Println(err)
return nil, err
}
fmt.Printf("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)
fmt.Println(err)
return nil, err
}
return svc, nil
}
func 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
}
fmt.Printf("Deleted service: %s\n", name)
return nil
}
func 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 {
fmt.Println(err)
return err
}
if err := waitForPodToDelete(clientset, namespace, name); err != nil {
return err
}
fmt.Printf("Deleted pod: %s\n", name)
return nil
}
func createPreflightTestPod(clientset *kubernetes.Clientset, namespace string, podName string, imageName 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": "demo",
},
},
Spec: apiv1.PodSpec{
Containers: []apiv1.Container{
{
Name: "cnt",
Image: imageName,
ImagePullPolicy: apiv1.PullIfNotPresent,
Command: []string{
"sleep",
"3600",
},
},
},
},
}
// now create the pod in kubernetes cluster using the clientset
if err := retryOnError(func() (err error) {
pod, err = clientset.CoreV1().Pods(namespace).Create(pod)
return err
}); err != nil {
fmt.Println(err)
return nil, err
}
fmt.Printf("Created pod: %s\n", pod.Name)
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 execute(method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error {
exec, err := remotecommand.NewSPDYExecutor(config, method, url)
if err != nil {
return err
}
return exec.Stream(remotecommand.StreamOptions{
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
Tty: tty,
})
}
func executeRemoteCommand(clientset *kubernetes.Clientset, config *rest.Config, podName, containerName, namespace string, command []string) (string, string, error) {
tty := false
req := clientset.CoreV1().RESTClient().Post().
Resource("pods").
Name(podName).
Namespace(namespace).
SubResource("exec").
Param("container", containerName)
req.VersionedParams(&apiv1.PodExecOptions{
Container: containerName,
Command: command,
Stdin: false,
Stdout: true,
Stderr: true,
TTY: tty,
}, scheme.ParameterCodec)
var stdout, stderr bytes.Buffer
err := execute("POST", req.URL(), config, nil, &stdout, &stderr, tty)
return strings.TrimSpace(stdout.String()), strings.TrimSpace(stderr.String()), err
}
func waitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDeployment *appsv1.Deployment) error {
timeout := time.NewTicker(2 * time.Minute)
defer timeout.Stop()
var d *appsv1.Deployment
var err error
WAIT:
for {
d, err = getDeployment(clientset, namespace, pfDeployment.GetName())
if err != nil {
err = fmt.Errorf("error: unable to retrieve deployment: %s\n", pfDeployment.GetName())
fmt.Println(err)
return err
}
select {
case <-timeout.C:
break WAIT
default:
if int(d.Status.ReadyReplicas) > 0 {
break WAIT
}
}
time.Sleep(5 * time.Second)
}
if int(d.Status.ReadyReplicas) == 0 {
err = fmt.Errorf("error: deployment took longer than expected to spin up pods")
fmt.Println(err)
return err
}
return nil
}
func waitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error {
var err error
if len(pod.Spec.Containers) > 0 {
timeout := time.NewTicker(2 * time.Minute)
defer timeout.Stop()
podName := pod.Name
OUT:
for {
pod, err = getPod(clientset, namespace, podName)
if err != nil {
err = fmt.Errorf("error: unable to retrieve %s pod by name", podName)
fmt.Println(err)
return err
}
select {
case <-timeout.C:
break OUT
default:
if len(pod.Status.ContainerStatuses) > 0 && pod.Status.ContainerStatuses[0].Ready {
break OUT
}
}
time.Sleep(5 * time.Second)
}
if len(pod.Status.ContainerStatuses) == 0 || !pod.Status.ContainerStatuses[0].Ready {
err = fmt.Errorf("error: container is taking much longer than expected")
fmt.Println(err)
return err
}
return nil
}
err = fmt.Errorf("error: there are no containers in the pod")
fmt.Println(err)
return err
}
func waitForPodToDelete(clientset *kubernetes.Clientset, namespace, podName string) error {
var err error
timeout := time.NewTicker(2 * time.Minute)
defer timeout.Stop()
OUT:
for {
_, err = getPod(clientset, namespace, podName)
if err != nil {
return nil
}
select {
case <-timeout.C:
break OUT
default:
}
time.Sleep(5 * time.Second)
}
err = fmt.Errorf("error: delete pod is taking unusually long")
fmt.Println(err)
return err
}
func waitForDeploymentToDelete(clientset *kubernetes.Clientset, namespace, deploymentName string) error {
var err error
timeout := time.NewTicker(2 * time.Minute)
defer timeout.Stop()
OUT:
for {
_, err = getDeployment(clientset, namespace, deploymentName)
if err != nil {
return nil
}
select {
case <-timeout.C:
break OUT
default:
}
time.Sleep(5 * time.Second)
}
err = fmt.Errorf("error: delete deployment is taking unusually long")
fmt.Println(err)
return err
}

View File

@@ -20,7 +20,7 @@ func Test_initiateK8sOps(t *testing.T) {
name: "valid case",
args: args{
opr: fmt.Sprintf("version"),
namespace: "ash-ns",
namespace: "test-ns",
},
wantErr: false,
},
@@ -28,7 +28,7 @@ func Test_initiateK8sOps(t *testing.T) {
name: "invalid case",
args: args{
opr: fmt.Sprintf("versions"),
namespace: "ash-ns",
namespace: "test-ns",
},
wantErr: true,
},
@@ -41,50 +41,3 @@ func Test_initiateK8sOps(t *testing.T) {
})
}
}
func Test_determinePlatformSpecificUrls(t *testing.T) {
type args struct {
platform string
}
tests := []struct {
name string
args args
want string
want1 string
wantErr bool
}{
{
name: "valid platform",
args: args{
platform: "windows",
},
want: fmt.Sprintf("%s%s", preflightBaseURL, preflightWindowsFile),
want1: preflightWindowsFile,
wantErr: false,
},
{
name: "invalid platform",
args: args{
platform: "unix",
},
want: "",
want1: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1, err := determinePlatformSpecificUrls(tt.args.platform)
if (err != nil) != tt.wantErr {
t.Errorf("determinePlatformSpecificUrls() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("determinePlatformSpecificUrls() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("determinePlatformSpecificUrls() got1 = %v, want %v", got1, tt.want1)
}
})
}
}

View File

@@ -0,0 +1,12 @@
package preflight
func (qp *QliksensePreflight) CreateRoleCheck(namespace string, kubeConfigContents []byte) error {
// create service account
// create role
// create rolebinding
return nil
}

View File

@@ -1,84 +1,56 @@
package preflight
import (
"bytes"
"fmt"
"io/ioutil"
"path/filepath"
"text/template"
"github.com/qlik-oss/sense-installer/pkg/api"
"github.com/Masterminds/semver/v3"
"k8s.io/apimachinery/pkg/version"
)
const minK8sVersion = "1.11.0"
const checkVersionYAML = `
apiVersion: troubleshoot.replicated.com/v1beta1
kind: Preflight
metadata:
name: cluster-preflight-checks
namespace: {{ .namespace }}
spec:
analyzers:
- clusterVersion:
outcomes:
- fail:
when: "< {{ .minK8sVersion }}"
message: The application requires at least Kubernetes {{ .minK8sVersion }} or later.
uri: https://www.kubernetes.io
- pass:
when: ">= {{ .minK8sVersion }}"
message: Good to go.
`
func (qp *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigContents []byte) error {
func (qp *QliksensePreflight) CheckK8sVersion() error {
// retrieve namespace
namespace := api.GetKubectlNamespace()
var currentVersion *semver.Version
api.LogDebugMessage("Namespace: %s\n", namespace)
tmpl, err := template.New("checkVersionYAML").Parse(checkVersionYAML)
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
if err != nil {
fmt.Printf("cannot parse template: %v", err)
err = fmt.Errorf("Unable to create clientset: %v\n", err)
return err
}
tempYaml, err := ioutil.TempFile("", "")
if err != nil {
fmt.Printf("cannot create file: %v", err)
var serverVersion *version.Info
if err := retryOnError(func() (err error) {
serverVersion, err = clientset.ServerVersion()
return err
}); err != nil {
err = fmt.Errorf("Unable to get server version: %v\n", err)
//fmt.Println(err)
return err
}
api.LogDebugMessage("Temp Yaml file: %s\n", tempYaml.Name())
fmt.Printf("Kubernetes API Server version: %s\n", serverVersion.String())
b := bytes.Buffer{}
err = tmpl.Execute(&b, map[string]string{
"namespace": namespace,
"minK8sVersion": minK8sVersion,
})
// Compare K8s version on the cluster with minimum supported k8s version
currentVersion, err = semver.NewVersion(serverVersion.String())
if err != nil {
err = fmt.Errorf("Unable to convert server version into semver version: %v\n", err)
//fmt.Println(err)
return err
}
//fmt.Printf("Current K8s Version: %v\n", currentVersion)
minK8sVersionSemver, err := semver.NewVersion(qp.GetPreflightConfigObj().GetMinK8sVersion())
if err != nil {
err = fmt.Errorf("Unable to convert minimum Kubernetes version into semver version:%v\n", err)
fmt.Println(err)
return err
}
tempYaml.WriteString(b.String())
//api.LogDebugMessage("Temp yaml contents: %s", b.String())
fmt.Printf("Minimum Kubernetes version supported: %s\n", minK8sVersion)
// current kubectl version
opr := fmt.Sprintf("version")
err = initiateK8sOps(opr, namespace)
if err != nil {
fmt.Println(err)
return err
if currentVersion.GreaterThan(minK8sVersionSemver) {
//fmt.Printf("\n\nCurrent %s Component version: %s is less than minimum required version:%s\n", component, currentComponentVersion, componentVersionFromDependenciesYaml)
fmt.Printf("Current %s is greater than minimum required version:%s\n", currentVersion, minK8sVersionSemver)
fmt.Println("Preflight minimum kubernetes version check: PASSED")
} else {
fmt.Printf("Current %s is less than minimum required version:%s\n", currentVersion, minK8sVersionSemver)
fmt.Println("Preflight minimum kubernetes version check: FAILED")
}
// call preflight
preflightCommand := filepath.Join(qp.Q.QliksenseHome, PreflightChecksDirName, preflightFileName)
err = invokePreflight(preflightCommand, tempYaml)
if err != nil {
fmt.Println(err)
return err
}
fmt.Println("Minimum kubernetes version check completed")
fmt.Printf("Completed Preflight kubernetes minimum version check\n")
return nil
}

View File

@@ -94,7 +94,7 @@ func (q *Qliksense) getConfigDirectory(gitUrl, gitRef, profileEntered string) (d
}
if gitRef != "" {
if dir, err = downloadFromGitRepoToTmpDir(gitUrl, gitRef); err != nil {
if dir, err = DownloadFromGitRepoToTmpDir(gitUrl, gitRef); err != nil {
return "", false, "", err
} else {
return dir, true, profile, nil
@@ -120,14 +120,15 @@ func (q *Qliksense) getConfigDirectory(gitUrl, gitRef, profileEntered string) (d
return dir, false, profile, nil
}
if dir, err = downloadFromGitRepoToTmpDir(gitUrl, "master"); err != nil {
if dir, err = DownloadFromGitRepoToTmpDir(gitUrl, "master"); err != nil {
return "", false, "", err
} else {
return dir, true, profile, nil
}
}
func downloadFromGitRepoToTmpDir(gitUrl, gitRef string) (string, error) {
//DownloadFromGitRepoToTmpDir download git repo to a temporary directory
func DownloadFromGitRepoToTmpDir(gitUrl, gitRef string) (string, error) {
if tmpDir, err := ioutil.TempDir("", ""); err != nil {
return "", err
} else {

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
@@ -88,7 +89,7 @@ func (q *Qliksense) applyConfigToK8s(qcr *qapi.QliksenseCR) error {
cr.GeneratePatches(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config"))
// apply generated manifests
profilePath := filepath.Join(qcr.Spec.GetManifestsRoot(), qcr.Spec.GetProfileDir())
mByte, err := executeKustomizeBuild(profilePath)
mByte, err := ExecuteKustomizeBuild(profilePath)
if err != nil {
fmt.Println("cannot generate manifests for "+profilePath, err)
return err
@@ -163,3 +164,54 @@ func (q *Qliksense) getCurrentCrDependentResourceAsString() (string, error) {
crString.WriteString("\n---\n")
return crString.String(), nil
}
func (q *Qliksense) EditCR(contextName string) error {
qConfig := qapi.NewQConfig(q.QliksenseHome)
if contextName == "" {
cr, err := qConfig.GetCurrentCR()
if err != nil {
return err
}
contextName = cr.GetName()
}
crFilePath := qConfig.GetCRFilePath(contextName)
tempFile, err := ioutil.TempFile("", "*.yaml")
if err != nil {
return err
}
crContent, err := ioutil.ReadFile(crFilePath)
if err != nil {
return err
}
if err := ioutil.WriteFile(tempFile.Name(), crContent, os.ModePerm); err != nil {
return nil
}
cmd := exec.Command(getKubeEditorTool(), tempFile.Name())
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
err = cmd.Run()
if err != nil {
return err
}
newCr, err := qapi.GetCRObject(tempFile.Name())
if err != nil {
return errors.New("cannot save the cr. Someting wrong in the file format. It is not saved\n" + err.Error())
}
oldCr, err := qapi.GetCRObject(crFilePath)
if oldCr.GetName() != newCr.GetName() {
return errors.New("cr name cannot be chagned")
}
if newCr.Validate() {
return qConfig.WriteCR(newCr)
}
return nil
}
func getKubeEditorTool() string {
editor := os.Getenv("KUBE_EDITOR")
if editor == "" {
editor = "vim"
}
return editor
}

View File

@@ -19,12 +19,24 @@ func (q *Qliksense) ViewCrds(opts *CrdCommandOptions) error {
fmt.Println("cannot get the current-context cr", err)
return err
}
if engineCRD, err := getQliksenseInitCrd(qcr); err != nil {
engineCRD, err := getQliksenseInitCrd(qcr)
if err != nil {
return err
} else if opts.All {
fmt.Printf("%s\n%s", q.GetOperatorCRDString(), engineCRD)
} else {
fmt.Printf("%s", engineCRD)
}
customCrd, err := getCustomCrd(qcr)
if err != nil {
return nil
}
fmt.Println(engineCRD)
if customCrd != "" {
fmt.Println("---")
fmt.Println(customCrd)
}
if opts.All {
fmt.Println("---")
fmt.Printf("%s", q.GetOperatorCRDString())
}
return nil
}
@@ -43,6 +55,14 @@ func (q *Qliksense) InstallCrds(opts *CrdCommandOptions) error {
} else if err = qapi.KubectlApply(engineCRD, ""); err != nil {
return err
}
if customCrd, err := getCustomCrd(qcr); err != nil {
return err
} else if customCrd != "" {
if err = qapi.KubectlApply(customCrd, ""); err != nil {
return err
}
}
if opts.All { // install opeartor crd
if err := qapi.KubectlApply(q.GetOperatorCRDString(), ""); err != nil {
fmt.Println("cannot do kubectl apply on opeartor CRD", err)
@@ -59,16 +79,29 @@ func getQliksenseInitCrd(qcr *qapi.QliksenseCR) (string, error) {
if qcr.Spec.GetManifestsRoot() != "" {
repoPath = qcr.Spec.GetManifestsRoot()
} else {
if repoPath, err = downloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master"); err != nil {
if repoPath, err = DownloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master"); err != nil {
return "", err
}
}
qInitMsPath := filepath.Join(repoPath, Q_INIT_CRD_PATH)
qInitByte, err := executeKustomizeBuild(qInitMsPath)
qInitByte, err := ExecuteKustomizeBuild(qInitMsPath)
if err != nil {
fmt.Println("cannot generate crds for qliksense-init", err)
return "", err
}
return string(qInitByte), nil
}
func getCustomCrd(qcr *qapi.QliksenseCR) (string, error) {
crdPath := qcr.GetCustomCrdsPath()
if crdPath == "" {
return "", nil
}
qInitByte, err := ExecuteKustomizeBuild(crdPath)
if err != nil {
fmt.Println("cannot generate custom crds", err)
return "", err
}
return string(qInitByte), nil
}

View File

@@ -8,7 +8,7 @@ import (
)
func TestGetQliksenseInitCrd(t *testing.T) {
someTmpRepoPath, err := downloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
someTmpRepoPath, err := DownloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@@ -1,6 +1,7 @@
package qliksense
import (
"errors"
"fmt"
"io/ioutil"
"os"
@@ -27,6 +28,29 @@ const (
imageSharedBlobsDirName = "blobs"
)
func (q *Qliksense) PullImages(version, profile string) error {
qConfig := qapi.NewQConfig(q.QliksenseHome)
if version != "" {
if err := q.FetchQK8s(version); err != nil {
return err
}
}
qcr, err := qConfig.GetCurrentCR()
if err != nil {
return err
}
if !qcr.IsRepoExist() {
return errors.New("ManifestsRoot not found")
}
if profile != "" {
qcr.Spec.Profile = profile
if e := qConfig.WriteCR(qcr); e != nil {
return e
}
}
return q.PullImagesForCurrentCR()
}
// PullImages ...
func (q *Qliksense) PullImagesForCurrentCR() error {
qConfig := qapi.NewQConfig(q.QliksenseHome)

View File

@@ -24,7 +24,7 @@ func (q *Qliksense) GetInstallableVersions(opts *LsRemoteCmdOptions) error {
if qcr.Spec.GetManifestsRoot() != "" {
repoPath = qcr.Spec.GetManifestsRoot()
} else {
repoPath, err = downloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
repoPath, err = DownloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
if err != nil {
return err
}

View File

@@ -1,8 +1,10 @@
package qliksense
import (
"bufio"
"log"
"os"
"strings"
"github.com/qlik-oss/sense-installer/pkg/api"
@@ -13,7 +15,8 @@ import (
"sigs.k8s.io/kustomize/api/types"
)
func executeKustomizeBuild(directory string) ([]byte, error) {
//ExecuteKustomizeBuild execute kustomize to the directory and return manifest as byte array
func ExecuteKustomizeBuild(directory string) ([]byte, error) {
return executeKustomizeBuildForFileSystem(directory, filesys.MakeFsOnDisk())
}
@@ -39,10 +42,26 @@ func executeKustomizeBuildForFileSystem(directory string, fSys filesys.FileSyste
func executeKustomizeBuildWithStdoutProgress(path string) (kuzManifest []byte, err error) {
result, err := api.ExecuteTaskWithBlinkingStdoutFeedback(func() (interface{}, error) {
return executeKustomizeBuild(path)
return ExecuteKustomizeBuild(path)
}, "...")
if err != nil {
return nil, err
}
return result.([]byte), nil
}
//GetYamlsFromMultiDoc filter yaml docs from multiyaml based on kind
func GetYamlsFromMultiDoc(multiYaml string, kind string) string {
yamlDocs := strings.Split(string(multiYaml), "---")
resultDocs := ""
for _, doc := range yamlDocs {
scanner := bufio.NewScanner(strings.NewReader(doc))
for scanner.Scan() {
if strings.HasPrefix(scanner.Text(), "kind: "+kind) {
resultDocs = resultDocs + "\n---\n" + doc
break
}
}
}
return resultDocs
}

View File

@@ -20,7 +20,7 @@ import (
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
)
func Test_executeKustomizeBuild(t *testing.T) {
func Test_ExecuteKustomizeBuild(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("unexpected error: %v\n", err)
@@ -41,7 +41,7 @@ configMapGenerator:
t.Fatalf("error writing kustomization file to path: %v error: %v\n", kustomizationYamlFilePath, err)
}
result, err := executeKustomizeBuild(tmpDir)
result, err := ExecuteKustomizeBuild(tmpDir)
if err != nil {
t.Fatalf("unexpected kustomize error: %v\n", err)
}
@@ -86,7 +86,7 @@ func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
generateKeys(cr, "won't-use")
yamlResources, err := executeKustomizeBuild(path.Join(configPath, "manifests", "base", "resources", "users"))
yamlResources, err := ExecuteKustomizeBuild(path.Join(configPath, "manifests", "base", "resources", "users"))
if err != nil {
t.Fatalf("unexpected kustomize error: %v\n", err)
}
@@ -139,3 +139,97 @@ func getEjsonKeyDir(defaultKeyDir string) string {
}
return ejsonKeyDir
}
func Test_GetYamlDocKindFromMultiDoc(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("unexpected error: %v\n", err)
}
defer os.RemoveAll(tmpDir)
kustomizationYamlFilePath := path.Join(tmpDir, "kustomization.yaml")
testResFileYamlFilePath := path.Join(tmpDir, "test-file.yaml")
kustomizationYaml := `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- test-file.yaml
`
testYaml := `
apiVersion: v1
data:
foo: bar
kind: ConfigMap
metadata:
name: foo-config
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: qix-sessions
chart: qix-sessions-4.0.10
heritage: Helm
release: qliksense
name: qliksense-qix-sessions
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
labels:
app: chronos
chart: chronos-1.5.7
heritage: Helm
release: qliksense
name: qliksense-chronos
namespace: default
rules:
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
- update
`
err = ioutil.WriteFile(kustomizationYamlFilePath, []byte(kustomizationYaml), os.ModePerm)
if err != nil {
t.Fatalf("error writing kustomization file to path: %v error: %v\n", kustomizationYamlFilePath, err)
}
err = ioutil.WriteFile(testResFileYamlFilePath, []byte(testYaml), os.ModePerm)
if err != nil {
t.Fatalf("error writing test-file to path: %v error: %v\n", testResFileYamlFilePath, err)
}
result, err := ExecuteKustomizeBuild(tmpDir)
if err != nil {
t.Fatalf("unexpected kustomize error: %v\n", err)
}
resultYaml := GetYamlsFromMultiDoc(string(result), "Role")
expectedK8sYaml := `
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
labels:
app: chronos
chart: chronos-1.5.7
heritage: Helm
release: qliksense
name: qliksense-chronos
namespace: default
rules:
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
- update
`
if resultYaml != expectedK8sYaml {
t.Fatalf("expected k8s yaml: [%v] but got: [%v]\n", expectedK8sYaml, resultYaml)
}
}