pull/3636/merge
Adam Reese 8 years ago committed by GitHub
commit 377240b798
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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

@ -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:

@ -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

@ -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

@ -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;)

@ -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")
}

@ -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())
})
})

@ -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`

@ -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
}

55
glide.lock generated

@ -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

@ -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

@ -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"

@ -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

@ -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
Loading…
Cancel
Save