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