diff --git a/Makefile b/Makefile index 231db271..19a7ba8d 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,9 @@ default: build-devserver test .PHONY: all all: build-devserver build-advancedserver +.PHONY: devserver +devserver: build-devserver test-devserver + # Build incubating components .PHONY: incubating incubating: build-explorer @@ -37,6 +40,7 @@ clean: rm -rf ./deps downloads/mqadv_dev903_ubuntu_x86-64.tar.gz: + $(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers"$(END))) mkdir -p downloads cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev903_ubuntu_x86-64.tar.gz @@ -46,8 +50,8 @@ downloads: downloads/mqadv_dev903_ubuntu_x86-64.tar.gz .PHONY: deps deps: glide install --strip-vendor - cd test/docker && dep ensure - cd test/kubernetes && dep ensure + cd test/docker && dep ensure -vendor-only + cd test/kubernetes && dep ensure -vendor-only build/runmqserver: mkdir -p build @@ -76,6 +80,7 @@ test-advancedserver: build .PHONY: test-devserver test-devserver: build + $(info $(SPACER)$(shell printf $(TITLE)"Test $(DOCKER_IMAGE_DEVSERVER)"$(END))) cd pkg/name && go test cd test/docker && TEST_IMAGE=$(DOCKER_IMAGE_DEVSERVER) go test @@ -109,11 +114,13 @@ endef .PHONY: build-advancedserver build-advancedserver: build downloads/CNJR7ML.tar.gz + $(info $(SPACER)$(shell printf $(TITLE)"Build $(DOCKER_IMAGE_ADVANCEDSERVER)"$(END))) $(call docker-build-mq,$(DOCKER_IMAGE_ADVANCEDSERVER),Dockerfile-server,CNJR7ML.tar.gz,"4486e8c4cc9146fd9b3ce1f14a2dfc5b","IBM MQ Advanced","9.0.3") docker tag $(DOCKER_IMAGE_ADVANCEDSERVER) mq-advancedserver:9.0.3-$(DOCKER_TAG_ARCH) .PHONY: build-devserver build-devserver: build downloads/mqadv_dev903_ubuntu_x86-64.tar.gz + $(info $(shell printf $(TITLE)"Build $(DOCKER_IMAGE_DEVSERVER)"$(END))) $(call docker-build-mq,$(DOCKER_IMAGE_DEVSERVER),Dockerfile-server,mqadv_dev903_ubuntu_x86-64.tar.gz,"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)","9.0.3") docker tag $(DOCKER_IMAGE_DEVSERVER) mq-devserver:9.0.3-$(DOCKER_TAG_ARCH) @@ -130,6 +137,8 @@ build-advancedserver-cover: build-advanced-server build-cov # build-web: build downloads/CNJR7ML.tar.gz # $(call docker-build-mq,mq-web:latest-$(DOCKER_TAG_ARCH),Dockerfile-mq-web) -.PHONY: build-devserver +.PHONY: build-explorer build-explorer: build downloads/mqadv_dev903_ubuntu_x86-64.tar.gz $(call docker-build-mq,mq-explorer:latest-$(DOCKER_TAG_ARCH),incubating/mq-explorer/Dockerfile-mq-explorer,mqadv_dev903_ubuntu_x86-64.tar.gz,"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)","9.0.3") + +include formatting.mk diff --git a/charts/ibm-mqadvanced-server-dev/Chart.yaml b/charts/ibm-mqadvanced-server-dev/Chart.yaml index 7166b6ac..2412b6ee 100644 --- a/charts/ibm-mqadvanced-server-dev/Chart.yaml +++ b/charts/ibm-mqadvanced-server-dev/Chart.yaml @@ -15,5 +15,5 @@ apiVersion: v1 description: IBM MQ queue manager name: ibm-mqadvanced-server-dev -version: 0.1.0 +version: 1.0.0-beta icon: https://developer.ibm.com/messaging/wp-content/uploads/sites/18/2017/07/IBM-MQ-Square-200.png diff --git a/charts/ibm-mqadvanced-server-dev/templates/stateful-set.yaml b/charts/ibm-mqadvanced-server-dev/templates/stateful-set.yaml index bf66475c..ae738762 100644 --- a/charts/ibm-mqadvanced-server-dev/templates/stateful-set.yaml +++ b/charts/ibm-mqadvanced-server-dev/templates/stateful-set.yaml @@ -66,7 +66,7 @@ spec: {{- if .Values.persistence.enabled }} volumeMounts: - mountPath: "/mnt/mqm" - name: {{ template "fullname" . }}-{{ .Values.persistence.name }} + name: "{{ template "fullname" . }}-{{ .Values.dataPVC.name }}" {{- end }} resources: limits: @@ -76,18 +76,25 @@ spec: volumeClaimTemplates: {{- if .Values.persistence.enabled }} - metadata: - name: {{ template "fullname" . }}-{{ .Values.persistence.name }} + name: "{{ template "fullname" . }}-{{ .Values.dataPVC.name }}" labels: app: {{ template "fullname" . }} chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" release: "{{ .Release.Name }}" heritage: "{{ .Release.Service }}" spec: - {{- if .Values.persistence.storageClassName }} - storageClassName: {{ .Values.persistence.storageClassName | quote }} + {{- if .Values.persistence.useDynamicProvisioning }} + # If present, use the storageClassName from the values.yaml, else use the + # default storageClass setup by Kubernetes Administrator + # + # Setting storageClassName to nil means use the default storage class + storageClassName: {{ default nil .Values.dataPVC.storageClassName | quote }} + {{- else }} + # Disable dynamic provisioning + storageClassName: "" {{- end }} accessModes: [ "ReadWriteOnce" ] resources: requests: - storage: {{ .Values.persistence.size | quote }} + storage: {{ .Values.dataPVC.size | quote }} {{- end }} diff --git a/charts/ibm-mqadvanced-server-dev/values.yaml b/charts/ibm-mqadvanced-server-dev/values.yaml index 32cc7745..75786e0a 100644 --- a/charts/ibm-mqadvanced-server-dev/values.yaml +++ b/charts/ibm-mqadvanced-server-dev/values.yaml @@ -14,8 +14,9 @@ # license must be set to "accept" to accept the terms of the IBM license license: "not accepted" + image: - # repository is the container repository to use, which must contain IBM MQ Advanced + # repository is the container repository to use, which must contain IBM MQ Advanced for Developers repository: ibmcom/mq # tag is the tag to use for the container repository tag: 9 @@ -23,18 +24,27 @@ image: pullSecret: # pullPolicy is either IfNotPresent or Always (https://kubernetes.io/docs/concepts/containers/images/) pullPolicy: IfNotPresent -# data section specifies settings for the main persistent volume claim, which is used for data in /var/mqm + +# persistence section specifies persistence settings which apply to the whole chart persistence: + # enabled is whether to use Persistent Volumes or not enabled: true - # name sets part of the name for this persistent volume claim + # useDynamicProvisioning is whether or not to use Storage Classes to dynamically create Persistent Volumes + useDynamicProvisioning: true + +# dataPVC section specifies settings for the main Persistent Volume Claim, which is used for data in /var/mqm +dataPVC: + # name sets part of the name for this Persistent Volume Claim name: "data" - ## storageClassName is the name of the storage class to use, or an empty string for no storage class + ## storageClassName is the name of the Storage Class to use, or an empty string for no Storage Class storageClassName: "" - ## size is the minimum size of the persistent volume + ## size is the minimum size of the Persistent Volume size: 2Gi + service: name: qmgr type: ClusterIP + resources: limits: cpu: 500m @@ -42,6 +52,7 @@ resources: requests: cpu: 500m memory: 512Mi + # queueManager section specifies settings for the MQ Queue Manager queueManager: # name allows you to specify the name to use for the queue manager. Defaults to the Helm release name. @@ -52,5 +63,6 @@ queueManager: adminPassword: # appPassword sets the password of the app user appPassword: + # nameOverride can be set to partially override the name of the resources created by this chart nameOverride: diff --git a/charts/ibm-mqadvanced-server-prod/Chart.yaml b/charts/ibm-mqadvanced-server-prod/Chart.yaml index e617bcf0..8ba32468 100644 --- a/charts/ibm-mqadvanced-server-prod/Chart.yaml +++ b/charts/ibm-mqadvanced-server-prod/Chart.yaml @@ -15,5 +15,5 @@ apiVersion: v1 description: IBM MQ queue manager name: ibm-mqadvanced-server-prod -version: 0.1.0 +version: 1.0.0-beta icon: https://developer.ibm.com/messaging/wp-content/uploads/sites/18/2017/07/IBM-MQ-Square-200.png diff --git a/charts/ibm-mqadvanced-server-prod/templates/stateful-set.yaml b/charts/ibm-mqadvanced-server-prod/templates/stateful-set.yaml index fdd854eb..6ba3c563 100644 --- a/charts/ibm-mqadvanced-server-prod/templates/stateful-set.yaml +++ b/charts/ibm-mqadvanced-server-prod/templates/stateful-set.yaml @@ -51,7 +51,7 @@ spec: {{- if .Values.persistence.enabled }} volumeMounts: - mountPath: "/mnt/mqm" - name: {{ template "fullname" . }}-{{ .Values.persistence.name }} + name: "{{ template "fullname" . }}-{{ .Values.dataPVC.name }}" {{- end }} # Set liveness probe to determine if the queue manager is running livenessProbe: @@ -67,7 +67,7 @@ spec: exec: command: - chkmqready - initialDelaySeconds: 5 + initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 1 @@ -79,18 +79,25 @@ spec: volumeClaimTemplates: {{- if .Values.persistence.enabled }} - metadata: - name: {{ template "fullname" . }}-{{ .Values.persistence.name }} + name: "{{ template "fullname" . }}-{{ .Values.dataPVC.name }}" labels: app: {{ template "fullname" . }} chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" release: "{{ .Release.Name }}" heritage: "{{ .Release.Service }}" spec: - {{- if .Values.persistence.storageClassName }} - storageClassName: {{ .Values.persistence.storageClassName | quote }} + {{- if .Values.persistence.useDynamicProvisioning }} + ## If present, use the storageClassName from the values.yaml, else use the + ## default storageClass setup by Kubernetes Administrator + ## + ## Setting storageClassName to nil means use the default storage class + storageClassName: {{ default nil .Values.dataPVC.storageClassName | quote }} + {{- else }} + ## Disable dynamic provisioning + storageClassName: "" {{- end }} accessModes: [ "ReadWriteOnce" ] resources: requests: - storage: {{ .Values.persistence.size | quote }} + storage: {{ .Values.dataPVC.size | quote }} {{- end }} diff --git a/charts/ibm-mqadvanced-server-prod/values.yaml b/charts/ibm-mqadvanced-server-prod/values.yaml index 7cf8b0cd..fe3d3216 100644 --- a/charts/ibm-mqadvanced-server-prod/values.yaml +++ b/charts/ibm-mqadvanced-server-prod/values.yaml @@ -14,6 +14,7 @@ # license must be set to "accept" to accept the terms of the IBM license license: "not accepted" + image: # repository is the container repository to use, which must contain IBM MQ Advanced repository: @@ -23,18 +24,27 @@ image: pullSecret: # pullPolicy is either IfNotPresent or Always (https://kubernetes.io/docs/concepts/containers/images/) pullPolicy: IfNotPresent -# persistence section specifies settings for the main persistent volume claim, which is used for data in /var/mqm + +# persistence section specifies persistence settings which apply to the whole chart persistence: + # enabled is whether to use Persistent Volumes or not enabled: true - # name sets part of the name for this persistent volume claim + # useDynamicProvisioning is whether or not to use Storage Classes to dynamically create Persistent Volumes + useDynamicProvisioning: true + +# dataPVC section specifies settings for the main Persistent Volume Claim, which is used for data in /var/mqm +dataPVC: + # name sets part of the name for this Persistent Volume Claim name: "data" - ## storageClassName is the name of the storage class to use, or an empty string for no storage class + # storageClassName is the name of the Storage Class to use, or an empty string for no Storage Class storageClassName: "" - ## size is the minimum size of the persistent volume + # size is the minimum size of the Persistent Volume size: 2Gi + service: name: qmgr type: ClusterIP + resources: limits: cpu: 1 @@ -42,11 +52,14 @@ resources: requests: cpu: 1 memory: 1Gi + # queueManager section specifies settings for the MQ Queue Manager queueManager: # name allows you to specify the name to use for the queue manager. Defaults to the Helm release name. name: + # nameOverride can be set to partially override the name of the resources created by this chart nameOverride: + # livenessDelay should be raised if your system cannot start the Queue Manager in 60 seconds livenessDelay: 60 diff --git a/formatting.mk b/formatting.mk new file mode 100644 index 00000000..1707fa1b --- /dev/null +++ b/formatting.mk @@ -0,0 +1,17 @@ +GREEN="\033[32m" +RED="\033[31m" +BLUE="\033[34m" +PURPLE="\033[35m" +AQUA="\033[36m" + +END="\033[0m" + +UNDERLINE="\033[4m" +BOLD="\033[1m" + +TITLE=$(BLUE)$(BOLD)$(UNDERLINE) + +define SPACER + + +endef diff --git a/test/kubernetes/Gopkg.lock b/test/kubernetes/Gopkg.lock index c7577dd3..c88f7aa8 100644 --- a/test/kubernetes/Gopkg.lock +++ b/test/kubernetes/Gopkg.lock @@ -200,10 +200,10 @@ revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f" [[projects]] - branch = "master" + branch = "release-1.7" name = "k8s.io/apimachinery" - packages = ["pkg/api/equality","pkg/api/errors","pkg/api/meta","pkg/api/resource","pkg/apimachinery","pkg/apimachinery/announced","pkg/apimachinery/registered","pkg/apis/meta/v1","pkg/apis/meta/v1/unstructured","pkg/apis/meta/v1alpha1","pkg/conversion","pkg/conversion/queryparams","pkg/conversion/unstructured","pkg/fields","pkg/labels","pkg/runtime","pkg/runtime/schema","pkg/runtime/serializer","pkg/runtime/serializer/json","pkg/runtime/serializer/protobuf","pkg/runtime/serializer/recognizer","pkg/runtime/serializer/streaming","pkg/runtime/serializer/versioning","pkg/selection","pkg/types","pkg/util/clock","pkg/util/diff","pkg/util/errors","pkg/util/framer","pkg/util/intstr","pkg/util/json","pkg/util/net","pkg/util/rand","pkg/util/runtime","pkg/util/sets","pkg/util/validation","pkg/util/validation/field","pkg/util/wait","pkg/util/yaml","pkg/version","pkg/watch","third_party/forked/golang/reflect"] - revision = "b166f81f5c4c88402ae23a0d0944c6ad08bffd3b" + packages = ["pkg/api/equality","pkg/api/errors","pkg/api/meta","pkg/api/resource","pkg/apimachinery","pkg/apimachinery/announced","pkg/apimachinery/registered","pkg/apis/meta/v1","pkg/apis/meta/v1/unstructured","pkg/apis/meta/v1alpha1","pkg/conversion","pkg/conversion/queryparams","pkg/conversion/unstructured","pkg/fields","pkg/labels","pkg/openapi","pkg/runtime","pkg/runtime/schema","pkg/runtime/serializer","pkg/runtime/serializer/json","pkg/runtime/serializer/protobuf","pkg/runtime/serializer/recognizer","pkg/runtime/serializer/streaming","pkg/runtime/serializer/versioning","pkg/selection","pkg/types","pkg/util/clock","pkg/util/diff","pkg/util/errors","pkg/util/framer","pkg/util/intstr","pkg/util/json","pkg/util/net","pkg/util/rand","pkg/util/runtime","pkg/util/sets","pkg/util/validation","pkg/util/validation/field","pkg/util/wait","pkg/util/yaml","pkg/version","pkg/watch","third_party/forked/golang/reflect"] + revision = "8ab5f3d8a330c2e9baaf84e39042db8d49034ae2" [[projects]] name = "k8s.io/client-go" @@ -211,15 +211,9 @@ revision = "d92e8497f71b7b4e0494e5bd204b48d34bd6f254" version = "v4.0.0" -[[projects]] - branch = "master" - name = "k8s.io/kube-openapi" - packages = ["pkg/common"] - revision = "abfc5fbe1cf87ee697db107fdfd24c32fe4397a8" - [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "05d4500518bb3fde554ca53f3d165ed3a20dec25bd4a24c6e59ee50dbea491b9" + inputs-digest = "caae962961e20347a5f6f9001a55d7baa94307f6bf6aecc001165a28a51e3919" solver-name = "gps-cdcl" solver-version = 1 diff --git a/test/kubernetes/Gopkg.toml b/test/kubernetes/Gopkg.toml index a8c1aed1..563e25d6 100644 --- a/test/kubernetes/Gopkg.toml +++ b/test/kubernetes/Gopkg.toml @@ -16,10 +16,14 @@ branch = "master" name = "golang.org/x/sys" -[[constraint]] - branch = "master" - name = "k8s.io/apimachinery" - +# The version of client-go corresponds to a specific Kubernetes version +# (e.g. client-go 4.0.0 is for kubernetes 1.7) +# If you change this version, you also need to update the corresponding +# branch for k8s.io/apimachinery (below) [[constraint]] name = "k8s.io/client-go" version = "4.0.0" + +[[constraint]] + name = "k8s.io/apimachinery" + branch = "release-1.7" diff --git a/test/kubernetes/helm_test.go b/test/kubernetes/helm_test.go index afb6aa8d..05cb69fd 100644 --- a/test/kubernetes/helm_test.go +++ b/test/kubernetes/helm_test.go @@ -16,131 +16,38 @@ limitations under the License. package main import ( - "os" - "os/exec" - "runtime" "strings" "testing" - "time" - "golang.org/x/sys/unix" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/pkg/api/v1" - "k8s.io/client-go/tools/clientcmd" ) -var namespace string = "default" - -// runCommand runs an OS command. On Linux it waits for the command to -// complete and returns the exit status (return code). -// TODO: duplicated from cmd/runmqserver/main.go -func runCommand(t *testing.T, name string, arg ...string) (string, int, error) { - t.Logf("Running command %v %v", name, arg) - cmd := exec.Command(name, arg...) - // Run the command and wait for completion - out, err := cmd.CombinedOutput() - if err != nil { - var rc int - // Only works on Linux - if runtime.GOOS == "linux" { - // func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) - var ws unix.WaitStatus - //var rusage syscall.Rusage - unix.Wait4(cmd.Process.Pid, &ws, 0, nil) - //ee := err.(*os.SyscallError) - //ws := ee.Sys().(syscall.WaitStatus) - rc = ws.ExitStatus() - } else { - rc = -1 - } - if rc == 0 { - return string(out), rc, nil - } - return string(out), rc, err - } - return string(out), 0, nil -} - -func helmDelete(t *testing.T, release string) { - out, _, err := runCommand(t, "helm", "delete", "--purge", release) - if err != nil { - t.Error(out) - t.Fatal(err) - } -} - -func helmDeleteWithPVC(t *testing.T, cs *kubernetes.Clientset, release string) { - helmDelete(t, release) - pvcs, err := cs.CoreV1().PersistentVolumeClaims(namespace).List(metav1.ListOptions{ - LabelSelector: "app=" + release + "-mq", - }) - if err != nil { - t.Fatal(err) - } - for _, pvc := range pvcs.Items { - t.Logf("Deleting persistent volume claim: %v", pvc.Name) - err := cs.CoreV1().PersistentVolumeClaims(namespace).Delete(pvc.Name, &metav1.DeleteOptions{}) - if err != nil { - t.Fatal(err) - } - } -} - -func kubeLogin(t *testing.T) *kubernetes.Clientset { - kc := os.Getenv("HOME") + "/.kube/config" - c, err := clientcmd.BuildConfigFromFlags("", kc) - if err != nil { - t.Fatal(err) - } - cs, err := kubernetes.NewForConfig(c) - if err != nil { - t.Fatal(err) - } - return cs -} - -func waitForTerminated(pods types.PodInterface, name string) (v1.PodPhase, error) { - status := v1.PodUnknown - var pod *v1.Pod - var err error - for status != v1.PodSucceeded && status != v1.PodFailed { - pod, err = pods.Get(name, metav1.GetOptions{}) - if err != nil { - return status, err - } - status = pod.Status.Phase - time.Sleep(1 * time.Second) - } - // The LastTerminationState doesn't seem to include an exit code - //t := pod.Status.ContainerStatuses[0].LastTerminationState - return status, nil -} +var namespace = "default" func TestHelmGoldenPath(t *testing.T) { cs := kubeLogin(t) - chart := "../charts/mqadvanced-dev" - image := "master.cfc:8500/default/mqadvanced" - tag := "latest" release := strings.ToLower(t.Name()) - out, _, err := runCommand(t, "helm", "install", chart, "--name", release, "--set", "license=accept", "--set", "image.repository="+image, "--set", "image.tag="+tag) - if err != nil { - t.Error(out) - t.Fatal(err) - } - defer helmDeleteWithPVC(t, cs, release) + helmInstall(t, cs, release, "license=accept", "persistence.useDynamicProvisioning=false") + defer helmDelete(t, release) + defer helmDeletePVC(t, cs, release) + waitForReady(t, cs, release) +} - nodes, err := cs.CoreV1().Nodes().List(metav1.ListOptions{}) - if err != nil { - t.Fatal(err) - } - t.Logf("There are %d nodes in the cluster\n", len(nodes.Items)) +func TestPersistenceDisabled(t *testing.T) { + cs := kubeLogin(t) + release := strings.ToLower(t.Name()) + helmInstall(t, cs, release, "license=accept", "persistence.enabled=false") + defer helmDelete(t, release) + waitForReady(t, cs, release) - pods, err := cs.CoreV1().Pods("").List(metav1.ListOptions{ - LabelSelector: "app=" + release + "-mq", + // Check that no PVCs were created + pvcs, err := cs.CoreV1().PersistentVolumeClaims(namespace).List(metav1.ListOptions{ + LabelSelector: "release=" + release, }) if err != nil { t.Fatal(err) } - t.Logf("There are %d pods with the right label in the cluster\n", len(pods.Items)) + if len(pvcs.Items) > 0 { + t.Errorf("Expected no PVC, found %v (%+v)", len(pvcs.Items), pvcs.Items) + } } diff --git a/test/kubernetes/helm_test_util.go b/test/kubernetes/helm_test_util.go new file mode 100644 index 00000000..cf7a3353 --- /dev/null +++ b/test/kubernetes/helm_test_util.go @@ -0,0 +1,186 @@ +/* +© Copyright IBM Corporation 2017 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "fmt" + "os" + "os/exec" + "runtime" + "strings" + "testing" + "time" + + "golang.org/x/sys/unix" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/pkg/api/v1" + "k8s.io/client-go/tools/clientcmd" +) + +// runCommand runs an OS command. On Linux it waits for the command to +// complete and returns the exit status (return code). +// TODO: duplicated from cmd/runmqserver/main.go +func runCommand(t *testing.T, name string, arg ...string) (string, int, error) { + t.Logf("Running command: %v %v", name, strings.Trim(fmt.Sprintf("%v", arg), "[]")) + cmd := exec.Command(name, arg...) + // Run the command and wait for completion + out, err := cmd.CombinedOutput() + if err != nil { + var rc int + // Only works on Linux + if runtime.GOOS == "linux" { + var ws unix.WaitStatus + unix.Wait4(cmd.Process.Pid, &ws, 0, nil) + rc = ws.ExitStatus() + } else { + rc = -1 + } + if rc == 0 { + return string(out), rc, nil + } + return string(out), rc, err + } + return string(out), 0, nil +} + +func helmInstall(t *testing.T, cs *kubernetes.Clientset, release string, values ...string) { + chart := "../../charts/ibm-mqadvanced-server-prod" + image := "mycluster.icp:8500/default/mq-devserver" + tag := "latest" + arg := []string{ + "install", + "--debug", + chart, + "--name", + release, + "--set", + "image.repository=" + image, + "--set", + "image.tag=" + tag, + "--set", + "image.pullSecret=admin.registrykey", + } + for _, value := range values { + arg = append(arg, "--set", value) + } + out, _, err := runCommand(t, "helm", arg...) + t.Log(out) + if err != nil { + t.Error(out) + t.Fatal(err) + } +} + +func helmDelete(t *testing.T, release string) { + out, _, err := runCommand(t, "helm", "delete", "--purge", release) + if err != nil { + t.Error(out) + t.Fatal(err) + } +} + +func helmDeletePVC(t *testing.T, cs *kubernetes.Clientset, release string) { + pvcs, err := cs.CoreV1().PersistentVolumeClaims(namespace).List(metav1.ListOptions{ + LabelSelector: "release=" + release, + }) + if err != nil { + t.Error(err) + } + for _, pvc := range pvcs.Items { + t.Logf("Deleting persistent volume claim: %v", pvc.Name) + err := cs.CoreV1().PersistentVolumeClaims(namespace).Delete(pvc.Name, &metav1.DeleteOptions{}) + if err != nil { + t.Fatal(err) + } + } +} + +func kubeLogin(t *testing.T) *kubernetes.Clientset { + kc := os.Getenv("HOME") + "/.kube/config" + c, err := clientcmd.BuildConfigFromFlags("", kc) + if err != nil { + t.Fatal(err) + } + cs, err := kubernetes.NewForConfig(c) + if err != nil { + t.Fatal(err) + } + return cs +} + +func kubeExec(t *testing.T, podName string, name string, arg ...string) (string, int, error) { + // Current version of Kubernetes Go client doesn't support "exec", so run this via the command line + param := []string{"exec", podName, "--", name} + param = append(param, arg...) + return runCommand(t, "kubectl", param...) +} + +func waitForReady(t *testing.T, cs *kubernetes.Clientset, release string) { + pods := getPodsForHelmRelease(t, cs, release) + if len(pods.Items) != 1 { + t.Fatalf("Expected 1 pod, found %v", len(pods.Items)) + } + pod := pods.Items[0] + podName := pod.Name + // Wait for the queue manager container to be started... + for { + pod, err := cs.CoreV1().Pods(namespace).Get(podName, metav1.GetOptions{}) + if err != nil { + t.Fatal(err) + } + if len(pod.Status.ContainerStatuses) > 0 { + // Got a container now, but it could still be in state "ContainerCreating" + // TODO: Check the status here properly + time.Sleep(3 * time.Second) + break + } + } + // Exec into the container to check if it's ready + for { + // Current version of Kubernetes Go client doesn't support "exec", so run this via the command line + // TODO: If we run "chkmqready" here, it doesn't seem to work + //out, _, err := runCommand(t, "kubectl", "exec", podName, "--", "dspmq") + out, _, err := kubeExec(t, podName, "dspmq") + //out, rc, err := runCommand(t, "kubectl", "exec", podName, "--", "chkmqready") + if err != nil { + t.Error(out) + out2, _, err2 := runCommand(t, "kubectl", "describe", "pod", podName) + if err2 == nil { + t.Log(out2) + } + t.Fatal(err) + } + if strings.Contains(out, "Running") { + t.Log("MQ is ready") + return + } + // if rc == 0 { + // return + // } + time.Sleep(1 * time.Second) + } +} + +func getPodsForHelmRelease(t *testing.T, cs *kubernetes.Clientset, release string) *v1.PodList { + pods, err := cs.CoreV1().Pods(namespace).List(metav1.ListOptions{ + LabelSelector: "release=" + release, + }) + if err != nil { + t.Fatal(err) + } + return pods +}