diff --git a/.circleci/bootstrap.sh b/.circleci/bootstrap.sh index 978464efe..3c345b159 100755 --- a/.circleci/bootstrap.sh +++ b/.circleci/bootstrap.sh @@ -15,5 +15,12 @@ # limitations under the License. set -euo pipefail -apt-get update -y && apt-get install -yq zip +apt-get update -y && apt-get install -yq zip socat + +echo "Install docker client" +VER="17.09.0-ce" +curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz +tar -xz -C /tmp -f /tmp/docker-$VER.tgz +mv /tmp/docker/* /usr/bin + make bootstrap diff --git a/.circleci/config.yml b/.circleci/config.yml index df9786d82..295383e7c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: working_directory: /go/src/k8s.io/helm - parallelism: 3 + parallelism: 4 docker: - image: golang:1.10 environment: @@ -18,6 +18,9 @@ jobs: - run: name: install dependencies command: .circleci/bootstrap.sh + - run: + name: update PATH + command: echo 'export PATH=~/.kubeadm-dind-cluster:$PATH' >> $BASH_ENV - save_cache: key: glide-{{ checksum "glide.yaml" }}-{{ checksum "glide.lock" }} paths: diff --git a/.circleci/deploy.sh b/.circleci/deploy.sh index 6ad91109d..65d0f6a08 100755 --- a/.circleci/deploy.sh +++ b/.circleci/deploy.sh @@ -33,12 +33,6 @@ else exit fi -echo "Install docker client" -VER="17.09.0-ce" -curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz -tar -xz -C /tmp -f /tmp/docker-$VER.tgz -mv /tmp/docker/* /usr/bin - echo "Install gcloud components" export CLOUDSDK_CORE_DISABLE_PROMPTS=1 curl https://sdk.cloud.google.com | bash diff --git a/.circleci/test.sh b/.circleci/test.sh index e0faf9c18..c4f0bba66 100755 --- a/.circleci/test.sh +++ b/.circleci/test.sh @@ -42,6 +42,10 @@ run_docs_check() { make verify-docs } +run_e2e_test() { + make e2e +} + # Build to ensure packages are compiled echo "Running 'make build'" make build @@ -50,4 +54,5 @@ case "${CIRCLE_NODE_INDEX-0}" in 0) run_unit_test ;; 1) run_style_check ;; 2) run_docs_check ;; + 3) run_e2e_test ;; esac diff --git a/Makefile b/Makefile index 709cc301a..251ae6fd0 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ APP = helm # go option GO ?= go -PKG := $(shell glide novendor) +PKG := ./pkg/... ./cmd/... TAGS := TESTS := . TESTFLAGS := @@ -117,6 +117,18 @@ clean: coverage: @scripts/coverage.sh +.PHONY: dind +dind: docker-build +dind: + @scripts/portforward.sh start + @scripts/dind.sh + @scripts/import-docker-image.sh + +.PHONY: e2e +e2e: dind +e2e: + go test -v ./e2e --cluster-url http://localhost:8080 --helm-bin ../bin/helm + HAS_GLIDE := $(shell command -v glide;) HAS_GOX := $(shell command -v gox;) HAS_GIT := $(shell command -v git;) diff --git a/e2e/e2e_suite_test.go b/e2e/e2e_suite_test.go new file mode 100644 index 000000000..7da6d1435 --- /dev/null +++ b/e2e/e2e_suite_test.go @@ -0,0 +1,28 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. +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 e2e_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestE2e(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "E2e Suite") +} diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go new file mode 100644 index 000000000..2d22e3388 --- /dev/null +++ b/e2e/e2e_test.go @@ -0,0 +1,78 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. +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 e2e + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +var _ = Describe("Basic Suite", func() { + var ( + helm HelmManager + namespace *v1.Namespace + clientset kubernetes.Interface + ) + + BeforeEach(func() { + var err error + clientset, err = KubeClient() + Expect(err).NotTo(HaveOccurred()) + By("Creating namespace and initializing test framework") + namespaceObj := &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "e2e-helm-", + }, + } + namespace, err = clientset.CoreV1().Namespaces().Create(namespaceObj) + Expect(err).NotTo(HaveOccurred()) + helm = &BinaryHelmManager{ + Namespace: namespace.Name, + Clientset: clientset, + HelmBin: helmBinPath, + TillerHost: tillerHost, + UseCanary: true, + UseServiceAccount: true, + } + if !localTiller { + Expect(helm.InstallTiller()).NotTo(HaveOccurred()) + } + }) + + AfterEach(func() { + By("Removing namespace") + DeleteNS(clientset, namespace) + }) + + It("Should be possible to create/delete/upgrade/rollback and check status of wordpress chart", func() { + chartName := "stable/wordpress" + By("Install chart stable/wordpress") + releaseName, err := helm.Install(chartName, nil) + Expect(err).NotTo(HaveOccurred()) + By("Check status of release " + releaseName) + Expect(helm.Status(releaseName)).NotTo(HaveOccurred()) + By("Upgrading release " + releaseName) + Expect(helm.Upgrade(chartName, releaseName, map[string]string{"image": "bitnami/wordpress:4.7.3-r1"})).NotTo(HaveOccurred()) + By("Rolling back release " + releaseName + "to a first revision") + Expect(helm.Rollback(releaseName, 1)).NotTo(HaveOccurred()) + By("Deleting release " + releaseName) + Expect(helm.Delete(releaseName)).NotTo(HaveOccurred()) + }) +}) diff --git a/e2e/helm_client.go b/e2e/helm_client.go new file mode 100644 index 000000000..ef5833e04 --- /dev/null +++ b/e2e/helm_client.go @@ -0,0 +1,268 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. +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 e2e + +import ( + "bytes" + "fmt" + "io/ioutil" + "os/exec" + "regexp" + "strconv" + "strings" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +// HelmManager provides functionality to install client/server helm and use it +type HelmManager interface { + // InstallTiller will bootstrap tiller pod in k8s + InstallTiller() error + // DeleteTiller removes tiller pod from k8s + DeleteTiller(removeHelmHome bool) error + // Install chart, returns releaseName and error + Install(chartName string, values map[string]string) (string, error) + // Status verifies state of installed release + Status(releaseName string) error + // Delete release + Delete(releaseName string) error + // Upgrade release + Upgrade(chartName, releaseName string, values map[string]string) error + // Rollback release + Rollback(releaseName string, revision int) error +} + +// BinaryHelmManager uses helm binary to work with helm server +type BinaryHelmManager struct { + Clientset kubernetes.Interface + Namespace string + HelmBin string + TillerHost string + UseCanary bool + UseServiceAccount bool +} + +func (m *BinaryHelmManager) InstallTiller() error { + args := []string{"init"} + if m.UseCanary { + args = append(args, "--canary-image") + } + if m.UseServiceAccount { + args = append(args, "--service-account", "tiller") + if err := m.InstallServiceAccounts(); err != nil { + return err + } + } + if _, err := m.executeUsingHelm(args...); err != nil { + return err + } + By("Waiting for tiller pod") + waitTillerPod(m.Clientset, m.Namespace) + return nil +} + +func (m *BinaryHelmManager) DeleteTiller(removeHelmHome bool) error { + args := []string{"reset", "--force"} + if removeHelmHome { + args = append(args, "--remove-helm-home") + } + _, err := m.executeUsingHelm(args...) + return err +} + +func (m *BinaryHelmManager) Install(chartName string, values map[string]string) (string, error) { + stdout, err := m.executeCommandWithValues(chartName, "install", values) + if err != nil { + return "", err + } + return getNameFromHelmOutput(stdout), nil +} + +// Status reports nil if release is considered to be succesfull +func (m *BinaryHelmManager) Status(releaseName string) error { + stdout, err := m.executeUsingHelm("status", releaseName) + if err != nil { + return err + } + status := getStatusFromHelmOutput(stdout) + if status == "DEPLOYED" { + return nil + } + return fmt.Errorf("Expected status is DEPLOYED. But got %v for release %v.", status, releaseName) +} + +func (m *BinaryHelmManager) Delete(releaseName string) error { + _, err := m.executeUsingHelm("delete", releaseName) + return err +} + +func (m *BinaryHelmManager) Upgrade(chartName, releaseName string, values map[string]string) error { + args := []string{"upgrade", releaseName, chartName} + if len(values) > 0 { + args = append(args, "--set", prepareArgsFromValues(values)) + } + _, err := m.executeUsingHelmInNamespace(args...) + return err +} + +func (m *BinaryHelmManager) Rollback(releaseName string, revision int) error { + _, err := m.executeUsingHelm("rollback", releaseName, strconv.Itoa(revision)) + return err +} + +func (m *BinaryHelmManager) executeUsingHelmInNamespace(args ...string) (string, error) { + return m.executeUsingHelm(append(args, "--namespace", m.Namespace)...) +} + +func (m *BinaryHelmManager) executeUsingHelm(args ...string) (string, error) { + if m.TillerHost != "" { + args = append(args, "--host", m.TillerHost) + } + return m.executeUsingBinary(m.HelmBin, append(args, "--tiller-namespace", m.Namespace)...) +} + +func (m *BinaryHelmManager) executeUsingBinary(binary string, args ...string) (string, error) { + cmd := exec.Command(binary, args...) + Logf("Running command %+v\n", cmd.Args) + stdout, err := cmd.Output() + if err != nil { + switch err.(type) { + case *exec.ExitError: + stderr := err.(*exec.ExitError) + Logf("Command %+v, Err %s\n", cmd.Args, stderr.Stderr) + case *exec.Error: + Logf("Command %+v, Err %s\n", cmd.Args, err) + } + return "", err + } + return string(stdout), nil +} + +func (m *BinaryHelmManager) executeCommandWithValues(releaseName, command string, values map[string]string) (string, error) { + args := []string{command, releaseName} + if len(values) > 0 { + vals := prepareArgsFromValues(values) + args = append(args, "--set", vals) + } + return m.executeUsingHelmInNamespace(args...) +} + +func (m *BinaryHelmManager) InstallServiceAccounts() error { + objects := strings.Replace(serviceAccountTemplate, "TILLER_NAMESPACE", m.Namespace, -1) + + f, err := ioutil.TempFile("", m.Namespace) + if err != nil { + Logf("Failed creating tempfile: %s", err) + return err + } + + f.WriteString(objects) + f.Sync() + + _, err = m.executeUsingBinary("kubectl", "create", "-f", f.Name()) + return err +} + +func regexpKeyFromStructuredOutput(key, output string) string { + r := regexp.MustCompile(fmt.Sprintf("%v:[[:space:]]*(.*)", key)) + // key will be captured in group with index 1 + result := r.FindStringSubmatch(output) + if len(result) < 2 { + return "" + } + return result[1] +} + +func getNameFromHelmOutput(output string) string { + return regexpKeyFromStructuredOutput("NAME", output) +} + +func getStatusFromHelmOutput(output string) string { + return regexpKeyFromStructuredOutput("STATUS", output) +} + +func waitTillerPod(clientset kubernetes.Interface, namespace string) { + Eventually(func() bool { + pods, err := clientset.CoreV1().Pods(namespace).List(metav1.ListOptions{}) + if err != nil { + return false + } + for _, pod := range pods.Items { + if !strings.Contains(pod.Name, "tiller") { + continue + } + Logf("Found tiller pod. Phase %v\n", pod.Status.Phase) + if pod.Status.Phase != v1.PodRunning { + return false + } + for _, cond := range pod.Status.Conditions { + if cond.Type != v1.PodReady { + continue + } + return cond.Status == v1.ConditionTrue + } + } + return false + }, 2*time.Minute, 5*time.Second).Should(BeTrue(), "tiller pod is not running in namespace "+namespace) +} + +func prepareArgsFromValues(values map[string]string) string { + var b bytes.Buffer + for key, val := range values { + b.WriteString(key) + b.WriteString("=") + b.WriteString(val) + b.WriteString(",") + } + return b.String() +} + +var serviceAccountTemplate = ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tiller + namespace: TILLER_NAMESPACE +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: tiller-manager + namespace: TILLER_NAMESPACE +rules: +- apiGroups: ["", "extensions", "apps", "*"] + resources: ["*"] + verbs: ["*"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: tiller-binding + namespace: TILLER_NAMESPACE +subjects: +- kind: ServiceAccount + name: tiller + namespace: TILLER_NAMESPACE +roleRef: + kind: Role + name: tiller-manager + apiGroup: rbac.authorization.k8s.io` diff --git a/e2e/utils.go b/e2e/utils.go new file mode 100644 index 000000000..f58ab0d4e --- /dev/null +++ b/e2e/utils.go @@ -0,0 +1,84 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. +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 e2e + +import ( + "flag" + "fmt" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +var ( + url string + tillerHost string + helmBinPath string + localTiller bool +) + +func init() { + flag.StringVar(&url, "cluster-url", "http://127.0.0.1:8080", "apiserver address to use with restclient") + flag.StringVar(&tillerHost, "tiller-host", "", "tiller address") + flag.StringVar(&helmBinPath, "helm-bin", "helm", "helm binary to test") + flag.BoolVar(&localTiller, "local-tiller", false, "wait for tiller pod") +} + +func LoadConfig() *rest.Config { + config, err := clientcmd.BuildConfigFromFlags(url, "") + Expect(err).NotTo(HaveOccurred()) + return config +} + +func KubeClient() (*kubernetes.Clientset, error) { + config := LoadConfig() + clientset, err := kubernetes.NewForConfig(config) + Expect(err).NotTo(HaveOccurred()) + return clientset, nil +} + +func DeleteNS(clientset kubernetes.Interface, namespace *v1.Namespace) { + defer GinkgoRecover() + err := clientset.CoreV1().Namespaces().Delete(namespace.Name, nil) + Expect(err).NotTo(HaveOccurred()) +} + +func Logf(format string, a ...interface{}) { + fmt.Fprintf(GinkgoWriter, format, a...) +} + +func WaitForPod(clientset kubernetes.Interface, namespace string, name string, phase v1.PodPhase) *v1.Pod { + defer GinkgoRecover() + var podUpdated *v1.Pod + Eventually(func() error { + podUpdated, err := clientset.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{}) + if err != nil { + return err + } + if phase != "" && podUpdated.Status.Phase != phase { + return fmt.Errorf("pod %v is not %v phase: %v", podUpdated.Name, phase, podUpdated.Status.Phase) + } + return nil + }, 1*time.Minute, 3*time.Second).Should(BeNil()) + return podUpdated +} diff --git a/glide.lock b/glide.lock index 1ccdae61c..427320367 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: d93f565214b112cf8560e9cd2da2f3ab7852a1f19544569fc112bd4fb2d1d506 -updated: 2018-03-08T14:06:06.497394911-08:00 +hash: 0e049d02297611e16d545f55796686953022556d452e3cc69f0c3dc6340c295c +updated: 2018-03-08T23:41:42.845903134Z imports: - name: cloud.google.com/go version: 3b1ae45394a234c385be014e9a488f2bb6eef821 @@ -247,6 +247,49 @@ imports: version: ad45545899c7b13c020ea92b2072220eefad42b8 - name: github.com/naoina/go-stringutil version: 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b +- name: github.com/onsi/ginkgo + version: 67b9df7f55fe1165fd9ad49aca7754cce01a42b8 + subpackages: + - config + - ginkgo + - ginkgo/convert + - ginkgo/interrupthandler + - ginkgo/nodot + - ginkgo/testrunner + - ginkgo/testsuite + - ginkgo/watch + - internal/codelocation + - internal/containernode + - internal/failer + - internal/leafnodes + - internal/remote + - internal/spec + - internal/spec_iterator + - internal/specrunner + - internal/suite + - internal/testingtproxy + - internal/writer + - reporters + - reporters/stenographer + - reporters/stenographer/support/go-colorable + - reporters/stenographer/support/go-isatty + - types +- name: github.com/onsi/gomega + version: d59fa0ac68bb5dd932ee8d24eed631cdd519efc3 + subpackages: + - format + - gstruct + - gstruct/errors + - internal/assertion + - internal/asyncassertion + - internal/oraclematcher + - internal/testingtsupport + - matchers + - matchers/support/goraph/bipartitegraph + - matchers/support/goraph/edge + - matchers/support/goraph/node + - matchers/support/goraph/util + - types - name: github.com/opencontainers/go-digest version: a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb - name: github.com/opencontainers/image-spec @@ -403,7 +446,7 @@ imports: - name: gopkg.in/yaml.v2 version: 53feefa2559fb8dfa8d81baad31be332c97d6c77 - name: k8s.io/api - version: 006a217681ae70cbacdd66a5e2fca1a61a8ff28e + version: acf347b865f29325eb61f4cd2df11e86e073a5ee subpackages: - admission/v1beta1 - admissionregistration/v1alpha1 @@ -440,7 +483,7 @@ imports: subpackages: - pkg/features - name: k8s.io/apimachinery - version: 68f9c3a1feb3140df59c67ced62d3a5df8e6c9c2 + version: 19e3f5aa3adca672c153d324e6b7d82ff8935f03 subpackages: - pkg/api/equality - pkg/api/errors @@ -496,7 +539,7 @@ imports: - third_party/forked/golang/netutil - third_party/forked/golang/reflect - name: k8s.io/apiserver - version: 2a1092aaa7202e8f9b188281ff9424a014ce61c2 + version: 40b00dd493d8fb98c255f0c6427f97fbf865a538 subpackages: - pkg/apis/audit - pkg/authentication/authenticator @@ -676,7 +719,7 @@ imports: - pkg/util/proto - pkg/util/proto/validation - name: k8s.io/kubernetes - version: 5fa2db2bd46ac79e5e00a4e6ed24191080aa463b + version: d2835416544f298c919e2ead3be3d0864b52323b subpackages: - pkg/api/events - pkg/api/legacyscheme diff --git a/glide.yaml b/glide.yaml index 7d0fce978..4c55cceb1 100644 --- a/glide.yaml +++ b/glide.yaml @@ -53,15 +53,15 @@ import: vcs: git - package: k8s.io/kubernetes - version: 1.9.2 + version: 1.9.3 - package: k8s.io/client-go version: ~6.0.0 - package: k8s.io/api - version: kubernetes-1.9.2 + version: kubernetes-1.9.3 - package: k8s.io/apimachinery - version: kubernetes-1.9.2 + version: kubernetes-1.9.3 - package: k8s.io/apiserver - version: kubernetes-1.9.2 + version: kubernetes-1.9.3 - package: cloud.google.com/go/compute repo: https://github.com/GoogleCloudPlatform/google-cloud-go.git @@ -71,3 +71,9 @@ testImports: version: ^1.1.4 subpackages: - assert +- package: github.com/onsi/ginkgo + version: ^1.4.0 + subpackages: + - ginkgo +- package: github.com/onsi/gomega + version: ^1.2.0 diff --git a/scripts/dind.sh b/scripts/dind.sh new file mode 100755 index 000000000..4c36a939d --- /dev/null +++ b/scripts/dind.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# Copyright 2017 The Kubernetes Authors All rights reserved. +# +# 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. +set -euo pipefail + +scripts/portforward.sh 8080 & + +wget https://cdn.rawgit.com/Mirantis/kubeadm-dind-cluster/master/fixed/dind-cluster-v1.9.sh +chmod +x dind-cluster-v1.9.sh +RUN_ON_BTRFS_ANYWAY=trololo bash -x ./dind-cluster-v1.9.sh up +export PATH="$HOME/.kubeadm-dind-cluster:$PATH" diff --git a/scripts/import-docker-image.sh b/scripts/import-docker-image.sh new file mode 100755 index 000000000..0be37f6db --- /dev/null +++ b/scripts/import-docker-image.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright 2017 The Kubernetes Authors All rights reserved. +# +# 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. +set -euxo pipefail + +IMAGE_REPO=${IMAGE_REPO:-gcr.io/kubernetes-helm/tiller} +IMAGE_TAG=${IMAGE_TAG:-canary} +TMP_IMAGE_PATH=${TMP_IMAGE_PATH:-/tmp/image.tar} +NODE_PATTERN=${NODE_PATTERN:-"kube-node-"} + +import-image() { + docker save "${IMAGE_REPO}:${IMAGE_TAG}" -o "${TMP_IMAGE_PATH}" + + for node in $(docker ps --format "{{.Names}}" | grep "${NODE_PATTERN}"); do + docker cp "${TMP_IMAGE_PATH}" "${node}:/image.tar" + docker exec -ti "${node}" docker load -i /image.tar + done + + set +o xtrace + echo "Finished copying docker image to dind nodes" +} + +import-image diff --git a/scripts/portforward.sh b/scripts/portforward.sh new file mode 100755 index 000000000..09001e554 --- /dev/null +++ b/scripts/portforward.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Copyright 2017 The Kubernetes Authors All rights reserved. +# +# 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. + +# Portforward hack for CircleCI remote docker +set -euxo pipefail + +if [[ ${1:-} == start ]]; then + docker run -d -it \ + --name portforward --net=host \ + --entrypoint /bin/sh \ + bobrik/socat -c "while true; do sleep 1000; done" +elif [[ ${1} ]]; then + socat "TCP-LISTEN:${1},reuseaddr,fork" \ + EXEC:"'docker exec -i portforward socat STDIO TCP-CONNECT:localhost:${1}'" +else + echo "Must specify either start or the port number" >&2 + exit 1 +fi