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" "github.com/spf13/cobra"
kerrors "k8s.io/kubernetes/pkg/api/errors" 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/helmpath"
"k8s.io/helm/cmd/helm/installer" "k8s.io/helm/cmd/helm/installer"
@ -70,7 +70,7 @@ type initCmd struct {
dryRun bool dryRun bool
out io.Writer out io.Writer
home helmpath.Home home helmpath.Home
kubeClient extensionsclient.DeploymentsGetter kubeClient internalclientset.Interface
} }
func newInitCmd(out io.Writer) *cobra.Command { 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 // runInit initializes local config and installs tiller to Kubernetes Cluster
func (i *initCmd) run() error { func (i *initCmd) run() error {
if flagDebug { 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 { if err != nil {
return err 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 { if i.dryRun {

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

@ -22,7 +22,10 @@ import (
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/apis/extensions" "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" extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
"k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/intstr"
@ -37,22 +40,46 @@ const defaultImage = "gcr.io/kubernetes-helm/tiller"
// command failed. // command failed.
// //
// If verbose is true, this will print the manifest to stdout. // If verbose is true, this will print the manifest to stdout.
func Install(client extensionsclient.DeploymentsGetter, namespace, image string, canary, verbose bool) error { func Install(client internalclientset.Interface, namespace, image string, canary, verbose bool) error {
obj := deployment(namespace, image, canary) if err := createDeployment(client.Extensions(), namespace, image, canary); err != nil {
_, err := client.Deployments(obj.Namespace).Create(obj) return err
}
if err := createService(client.Core(), namespace); err != nil {
return err return err
}
return nil
} }
//
// Upgrade uses kubernetes client to upgrade tiller to current version // Upgrade uses kubernetes client to upgrade tiller to current version
// //
// Returns an error if the command failed. // Returns an error if the command failed.
func Upgrade(client extensionsclient.DeploymentsGetter, namespace, image string, canary bool) error { func Upgrade(client internalclientset.Interface, namespace, image string, canary bool) error {
obj, err := client.Deployments(namespace).Get("tiller-deploy") obj, err := client.Extensions().Deployments(namespace).Get("tiller-deploy")
if err != nil { if err != nil {
return err return err
} }
obj.Spec.Template.Spec.Containers[0].Image = selectImage(image, canary) 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 return err
} }
@ -61,6 +88,18 @@ func deployment(namespace, image string, canary bool) *extensions.Deployment {
return generateDeployment(namespace, selectImage(image, canary)) 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 { func selectImage(image string, canary bool) string {
switch { switch {
case canary: case canary:
@ -80,6 +119,15 @@ func DeploymentManifest(namespace, image string, canary bool) (string, error) {
return string(buf), err 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 { func generateLabels(labels map[string]string) map[string]string {
labels["app"] = "helm" labels["app"] = "helm"
return labels return labels
@ -139,3 +187,26 @@ func generateDeployment(namespace, image string) *extensions.Deployment {
} }
return d 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" "github.com/ghodss/yaml"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
testcore "k8s.io/kubernetes/pkg/client/testing/core" 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) { func TestInstall(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0" image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
@ -80,13 +96,25 @@ func TestInstall(t *testing.T) {
} }
return true, obj, nil 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) t.Errorf("unexpected error: %#+v", err)
} }
if actions := fc.Actions(); len(actions) != 1 { if actions := fc.Actions(); len(actions) != 2 {
t.Errorf("unexpected actions: %v, expected 1 actions got %d", actions, len(actions)) 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 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) t.Errorf("unexpected error: %#+v", err)
} }
if actions := fc.Actions(); len(actions) != 1 { if actions := fc.Actions(); len(actions) != 2 {
t.Errorf("unexpected actions: %v, expected 1 actions got %d", actions, len(actions)) 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" image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
existingDeployment := deployment(api.NamespaceDefault, "imageToReplace", false) existingDeployment := deployment(api.NamespaceDefault, "imageToReplace", false)
existingService := service(api.NamespaceDefault)
fc := &fake.Clientset{} fc := &fake.Clientset{}
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { 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 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) t.Errorf("unexpected error: %#+v", err)
} }
if actions := fc.Actions(); len(actions) != 2 { if actions := fc.Actions(); len(actions) != 3 {
t.Errorf("unexpected actions: %v, expected 2 actions got %d", actions, len(actions)) 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