Create a Service definition for using Tiller in-cluster

pull/1822/head
Ferran Rodenas 8 years ago
parent 484a1eba72
commit d90a3a2260

@ -24,7 +24,7 @@ import (
"github.com/spf13/cobra"
kerrors "k8s.io/kubernetes/pkg/api/errors"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/helm/cmd/helm/helmpath"
"k8s.io/helm/cmd/helm/installer"
@ -70,7 +70,7 @@ type initCmd struct {
dryRun bool
out io.Writer
home helmpath.Home
kubeClient extensionsclient.DeploymentsGetter
kubeClient internalclientset.Interface
}
func newInitCmd(out io.Writer) *cobra.Command {
@ -105,11 +105,16 @@ func newInitCmd(out io.Writer) *cobra.Command {
// runInit initializes local config and installs tiller to Kubernetes Cluster
func (i *initCmd) run() error {
if flagDebug {
m, err := installer.DeploymentManifest(i.namespace, i.image, i.canary)
dm, err := installer.DeploymentManifest(i.namespace, i.image, i.canary)
if err != nil {
return err
}
fmt.Fprintln(i.out, m)
fmt.Fprintln(i.out, dm)
sm, err := installer.ServiceManifest(i.namespace)
if err != nil {
return err
}
fmt.Fprintln(i.out, sm)
}
if i.dryRun {

@ -47,15 +47,21 @@ func TestInitCmd(t *testing.T) {
cmd := &initCmd{
out: &buf,
home: helmpath.Home(home),
kubeClient: fc.Extensions(),
kubeClient: fc,
namespace: api.NamespaceDefault,
}
if err := cmd.run(); err != nil {
t.Errorf("expected error: %v", err)
}
action := fc.Actions()[0]
if !action.Matches("create", "deployments") {
t.Errorf("unexpected action: %v, expected create deployment", action)
actions := fc.Actions()
if len(actions) != 2 {
t.Errorf("Expected 2 actions, got %d", len(actions))
}
if !actions[0].Matches("create", "deployments") {
t.Errorf("unexpected action: %v, expected create deployment", actions[0])
}
if !actions[1].Matches("create", "services") {
t.Errorf("unexpected action: %v, expected create service", actions[1])
}
expected := "Tiller (the helm server side component) has been installed into your Kubernetes Cluster."
if !strings.Contains(buf.String(), expected) {
@ -63,7 +69,7 @@ func TestInitCmd(t *testing.T) {
}
}
func TestInitCmd_exsits(t *testing.T) {
func TestInitCmd_exists(t *testing.T) {
home, err := ioutil.TempDir("", "helm_home")
if err != nil {
t.Fatal(err)
@ -83,7 +89,7 @@ func TestInitCmd_exsits(t *testing.T) {
cmd := &initCmd{
out: &buf,
home: helmpath.Home(home),
kubeClient: fc.Extensions(),
kubeClient: fc,
namespace: api.NamespaceDefault,
}
if err := cmd.run(); err != nil {
@ -108,7 +114,7 @@ func TestInitCmd_clientOnly(t *testing.T) {
cmd := &initCmd{
out: &buf,
home: helmpath.Home(home),
kubeClient: fc.Extensions(),
kubeClient: fc,
clientOnly: true,
namespace: api.NamespaceDefault,
}
@ -142,7 +148,7 @@ func TestInitCmd_dryRun(t *testing.T) {
cmd := &initCmd{
out: &buf,
home: helmpath.Home(home),
kubeClient: fc.Extensions(),
kubeClient: fc,
clientOnly: true,
dryRun: true,
namespace: api.NamespaceDefault,

@ -22,7 +22,10 @@ import (
"github.com/ghodss/yaml"
"k8s.io/kubernetes/pkg/api"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
"k8s.io/kubernetes/pkg/util/intstr"
@ -37,22 +40,46 @@ const defaultImage = "gcr.io/kubernetes-helm/tiller"
// command failed.
//
// If verbose is true, this will print the manifest to stdout.
func Install(client extensionsclient.DeploymentsGetter, namespace, image string, canary, verbose bool) error {
obj := deployment(namespace, image, canary)
_, err := client.Deployments(obj.Namespace).Create(obj)
return err
func Install(client internalclientset.Interface, namespace, image string, canary, verbose bool) error {
if err := createDeployment(client.Extensions(), namespace, image, canary); err != nil {
return err
}
if err := createService(client.Core(), namespace); err != nil {
return err
}
return nil
}
//
// Upgrade uses kubernetes client to upgrade tiller to current version
//
// Returns an error if the command failed.
func Upgrade(client extensionsclient.DeploymentsGetter, namespace, image string, canary bool) error {
obj, err := client.Deployments(namespace).Get("tiller-deploy")
func Upgrade(client internalclientset.Interface, namespace, image string, canary bool) error {
obj, err := client.Extensions().Deployments(namespace).Get("tiller-deploy")
if err != nil {
return err
}
obj.Spec.Template.Spec.Containers[0].Image = selectImage(image, canary)
_, err = client.Deployments(namespace).Update(obj)
if _, err := client.Extensions().Deployments(namespace).Update(obj); err != nil {
return err
}
// If the service does not exists that would mean we are upgrading from a tiller version
// that didn't deploy the service, so install it.
if _, err := client.Core().Services(namespace).Get("tiller-deploy"); err != nil {
if !kerrors.IsNotFound(err) {
return err
}
if err := createService(client.Core(), namespace); err != nil {
return err
}
}
return nil
}
// createDeployment creates the Tiller deployment reource
func createDeployment(client extensionsclient.DeploymentsGetter, namespace, image string, canary bool) error {
obj := deployment(namespace, image, canary)
_, err := client.Deployments(obj.Namespace).Create(obj)
return err
}
@ -61,6 +88,18 @@ func deployment(namespace, image string, canary bool) *extensions.Deployment {
return generateDeployment(namespace, selectImage(image, canary))
}
// createService creates the Tiller service resource
func createService(client internalversion.ServicesGetter, namespace string) error {
obj := service(namespace)
_, err := client.Services(obj.Namespace).Create(obj)
return err
}
// service gets the service object that installs Tiller.
func service(namespace string) *api.Service {
return generateService(namespace)
}
func selectImage(image string, canary bool) string {
switch {
case canary:
@ -80,6 +119,15 @@ func DeploymentManifest(namespace, image string, canary bool) (string, error) {
return string(buf), err
}
// ServiceManifest gets the manifest (as a string) that describes the Tiller Service
// resource.
func ServiceManifest(namespace string) (string, error) {
obj := service(namespace)
buf, err := yaml.Marshal(obj)
return string(buf), err
}
func generateLabels(labels map[string]string) map[string]string {
labels["app"] = "helm"
return labels
@ -139,3 +187,26 @@ func generateDeployment(namespace, image string) *extensions.Deployment {
}
return d
}
func generateService(namespace string) *api.Service {
labels := generateLabels(map[string]string{"name": "tiller"})
s := &api.Service{
ObjectMeta: api.ObjectMeta{
Namespace: namespace,
Name: "tiller-deploy",
Labels: labels,
},
Spec: api.ServiceSpec{
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{
{
Name: "tiller",
Port: 44134,
TargetPort: intstr.FromString("tiller"),
},
},
Selector: labels,
},
}
return s
}

@ -22,6 +22,7 @@ import (
"github.com/ghodss/yaml"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
testcore "k8s.io/kubernetes/pkg/client/testing/core"
@ -64,6 +65,21 @@ func TestDeploymentManifest(t *testing.T) {
}
}
func TestServiceManifest(t *testing.T) {
o, err := ServiceManifest(api.NamespaceDefault)
if err != nil {
t.Fatalf("error %q", err)
}
var svc api.Service
if err := yaml.Unmarshal([]byte(o), &svc); err != nil {
t.Fatalf("error %q", err)
}
if got := svc.ObjectMeta.Namespace; got != api.NamespaceDefault {
t.Errorf("expected namespace %s, got %s", api.NamespaceDefault, got)
}
}
func TestInstall(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
@ -80,13 +96,25 @@ func TestInstall(t *testing.T) {
}
return true, obj, nil
})
fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.CreateAction).GetObject().(*api.Service)
l := obj.GetLabels()
if reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
t.Errorf("expected labels = '', got '%s'", l)
}
n := obj.ObjectMeta.Namespace
if n != api.NamespaceDefault {
t.Errorf("expected namespace = '%s', got '%s'", api.NamespaceDefault, n)
}
return true, obj, nil
})
if err := Install(fc.Extensions(), api.NamespaceDefault, image, false, false); err != nil {
if err := Install(fc, api.NamespaceDefault, image, false, false); err != nil {
t.Errorf("unexpected error: %#+v", err)
}
if actions := fc.Actions(); len(actions) != 1 {
t.Errorf("unexpected actions: %v, expected 1 actions got %d", actions, len(actions))
if actions := fc.Actions(); len(actions) != 2 {
t.Errorf("unexpected actions: %v, expected 2 actions got %d", actions, len(actions))
}
}
@ -100,13 +128,17 @@ func TestInstall_canary(t *testing.T) {
}
return true, obj, nil
})
fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.CreateAction).GetObject().(*api.Service)
return true, obj, nil
})
if err := Install(fc.Extensions(), api.NamespaceDefault, "", true, false); err != nil {
if err := Install(fc, api.NamespaceDefault, "", true, false); err != nil {
t.Errorf("unexpected error: %#+v", err)
}
if actions := fc.Actions(); len(actions) != 1 {
t.Errorf("unexpected actions: %v, expected 1 actions got %d", actions, len(actions))
if actions := fc.Actions(); len(actions) != 2 {
t.Errorf("unexpected actions: %v, expected 2 actions got %d", actions, len(actions))
}
}
@ -114,6 +146,7 @@ func TestUpgrade(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
existingDeployment := deployment(api.NamespaceDefault, "imageToReplace", false)
existingService := service(api.NamespaceDefault)
fc := &fake.Clientset{}
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
@ -127,12 +160,53 @@ func TestUpgrade(t *testing.T) {
}
return true, obj, nil
})
fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) {
return true, existingService, nil
})
if err := Upgrade(fc.Extensions(), api.NamespaceDefault, image, false); err != nil {
if err := Upgrade(fc, api.NamespaceDefault, image, false); err != nil {
t.Errorf("unexpected error: %#+v", err)
}
if actions := fc.Actions(); len(actions) != 2 {
t.Errorf("unexpected actions: %v, expected 2 actions got %d", actions, len(actions))
if actions := fc.Actions(); len(actions) != 3 {
t.Errorf("unexpected actions: %v, expected 3 actions got %d", actions, len(actions))
}
}
func TestUpgrade_serviceNotFound(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
existingDeployment := deployment(api.NamespaceDefault, "imageToReplace", false)
fc := &fake.Clientset{}
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
return true, existingDeployment, nil
})
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*extensions.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
}
return true, obj, nil
})
fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) {
return true, nil, errors.NewNotFound(api.Resource("services"), "1")
})
fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.CreateAction).GetObject().(*api.Service)
n := obj.ObjectMeta.Namespace
if n != api.NamespaceDefault {
t.Errorf("expected namespace = '%s', got '%s'", api.NamespaceDefault, n)
}
return true, obj, nil
})
if err := Upgrade(fc, api.NamespaceDefault, image, false); err != nil {
t.Errorf("unexpected error: %#+v", err)
}
if actions := fc.Actions(); len(actions) != 4 {
t.Errorf("unexpected actions: %v, expected 4 actions got %d", actions, len(actions))
}
}

Loading…
Cancel
Save