mirror of https://github.com/helm/helm
parent
ef64d1e5c7
commit
1dd3e75b16
@ -0,0 +1,194 @@
|
||||
// Copyright 2017 Mirantis
|
||||
//
|
||||
// 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 (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/pkg/api/v1"
|
||||
|
||||
"strings"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const (
|
||||
experimentalTillerImage string = "nebril/tiller-experimental"
|
||||
rudderAppcontroller string = "rudderAppcontroller"
|
||||
)
|
||||
|
||||
// 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) (string, error)
|
||||
// Status verifies state of installed release
|
||||
Status(releaseName string) error
|
||||
}
|
||||
|
||||
// BinaryHelmManager uses helm binary to work with helm server
|
||||
type BinaryHelmManager struct {
|
||||
Clientset kubernetes.Interface
|
||||
Namespace string
|
||||
HelmBin string
|
||||
}
|
||||
|
||||
func (m *BinaryHelmManager) InstallTiller() error {
|
||||
arg := make([]string, 0, 5)
|
||||
arg = append(arg, "init", "--tiller-namespace", m.Namespace)
|
||||
if enableRudder {
|
||||
arg = append(arg, "--tiller-image", experimentalTillerImage)
|
||||
}
|
||||
_, err := m.executeUsingHelm(arg...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
By("Waiting for tiller pod")
|
||||
waitTillerPod(m.Clientset, m.Namespace)
|
||||
if enableRudder {
|
||||
return prepareRudder(m.Clientset, m.Namespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BinaryHelmManager) DeleteTiller(removeHelmHome bool) error {
|
||||
arg := make([]string, 0, 4)
|
||||
arg = append(arg, "reset", "--tiller-namespace", m.Namespace)
|
||||
if removeHelmHome {
|
||||
arg = append(arg, "--remove-helm-home")
|
||||
}
|
||||
_, err := m.executeUsingHelm(arg...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if enableRudder {
|
||||
return deleteRudder(m.Clientset, m.Namespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BinaryHelmManager) Install(chartName string) (string, error) {
|
||||
stdout, err := m.executeUsingHelmInNamespace("install", chartName)
|
||||
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.executeUsingHelmInNamespace("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) executeUsingHelmInNamespace(arg ...string) (string, error) {
|
||||
arg = append(arg, "--namespace", m.Namespace, "--tiller-namespace", m.Namespace)
|
||||
return m.executeUsingHelm(arg...)
|
||||
}
|
||||
|
||||
func (m *BinaryHelmManager) executeUsingHelm(arg ...string) (string, error) {
|
||||
Logf("Running command %+v\n", arg)
|
||||
cmd := exec.Command(m.HelmBin, arg...)
|
||||
stdout, err := cmd.Output()
|
||||
if err != nil {
|
||||
stderr := err.(*exec.ExitError)
|
||||
Logf("Ccommand %+v, Err %s\n", arg, stderr.Stderr)
|
||||
return "", err
|
||||
}
|
||||
return string(stdout), nil
|
||||
}
|
||||
|
||||
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 prepareRudder(clientset kubernetes.Interface, namespace string) error {
|
||||
rudder := &v1.Pod{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: rudderAppcontroller,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
RestartPolicy: "Always",
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "rudder-appcontroller",
|
||||
Image: "mirantis/k8s-appcontroller",
|
||||
ImagePullPolicy: v1.PullNever,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := clientset.Core().Pods(namespace).Create(rudder)
|
||||
return err
|
||||
}
|
||||
|
||||
func deleteRudder(clientset kubernetes.Interface, namespace string) error {
|
||||
return clientset.Core().Pods(namespace).Delete(rudderAppcontroller, nil)
|
||||
}
|
||||
|
||||
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.Core().Pods(namespace).List(v1.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)
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
// Copyright 2017 Mirantis
|
||||
//
|
||||
// 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"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/pkg/api/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
// TODO move this variables under single object
|
||||
var url string
|
||||
var enableRudder bool
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&url, "cluster-url", "http://127.0.0.1:8080", "apiserver address to use with restclient")
|
||||
flag.BoolVar(&enableRudder, "use-rudder", false, "Use to enable rudder")
|
||||
}
|
||||
|
||||
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()
|
||||
pods, err := clientset.Core().Pods(namespace.Name).List(v1.ListOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
for _, pod := range pods.Items {
|
||||
clientset.Core().Pods(namespace.Name).Delete(pod.Name, nil)
|
||||
}
|
||||
clientset.Core().Namespaces().Delete(namespace.Name, nil)
|
||||
}
|
||||
|
||||
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.Core().Pods(namespace).Get(name)
|
||||
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
|
||||
}).Should(BeNil())
|
||||
return podUpdated
|
||||
}
|
Loading…
Reference in new issue