From 2f252e95242849123fa7826748cbfd050fde870c Mon Sep 17 00:00:00 2001
From: Yaroslav Molochko <y.molochko@anchorfree.com>
Date: Tue, 6 Mar 2018 02:21:17 +0200
Subject: [PATCH] Add --replicas option for HA fixes #2334

---
 cmd/helm/init.go                   |  3 +++
 cmd/helm/installer/install.go      |  1 +
 cmd/helm/installer/install_test.go | 27 +++++++++++++++++++++++++++
 cmd/helm/installer/options.go      | 13 +++++++++++++
 docs/helm/helm_init.md             |  3 ++-
 5 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/cmd/helm/init.go b/cmd/helm/init.go
index cccd8a83d..49adfc2c3 100644
--- a/cmd/helm/init.go
+++ b/cmd/helm/init.go
@@ -86,6 +86,7 @@ type initCmd struct {
 	kubeClient     kubernetes.Interface
 	serviceAccount string
 	maxHistory     int
+	replicas       int
 	wait           bool
 }
 
@@ -130,6 +131,7 @@ func newInitCmd(out io.Writer) *cobra.Command {
 	f.BoolVar(&i.opts.EnableHostNetwork, "net-host", false, "install Tiller with net=host")
 	f.StringVar(&i.serviceAccount, "service-account", "", "name of service account")
 	f.IntVar(&i.maxHistory, "history-max", 0, "limit the maximum number of revisions saved per release. Use 0 for no limit.")
+	f.IntVar(&i.replicas, "replicas", 1, "amount of tiller instances to run on the cluster")
 
 	f.StringVar(&i.opts.NodeSelectors, "node-selectors", "", "labels to specify the node on which Tiller is installed (app=tiller,helm=rocks)")
 	f.VarP(&i.opts.Output, "output", "o", "skip installation and output Tiller's manifest in specified format (json or yaml)")
@@ -175,6 +177,7 @@ func (i *initCmd) run() error {
 	i.opts.ForceUpgrade = i.forceUpgrade
 	i.opts.ServiceAccount = i.serviceAccount
 	i.opts.MaxHistory = i.maxHistory
+	i.opts.Replicas = i.replicas
 
 	writeYAMLManifest := func(apiVersion, kind, body string, first, last bool) error {
 		w := i.out
diff --git a/cmd/helm/installer/install.go b/cmd/helm/installer/install.go
index 230c7b39b..fc81fa26b 100644
--- a/cmd/helm/installer/install.go
+++ b/cmd/helm/installer/install.go
@@ -183,6 +183,7 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
 			Labels:    labels,
 		},
 		Spec: v1beta1.DeploymentSpec{
+			Replicas: opts.getReplicas(),
 			Template: v1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
 					Labels: labels,
diff --git a/cmd/helm/installer/install_test.go b/cmd/helm/installer/install_test.go
index eaea05870..dbb7143e3 100644
--- a/cmd/helm/installer/install_test.go
+++ b/cmd/helm/installer/install_test.go
@@ -211,6 +211,10 @@ func TestInstall(t *testing.T) {
 		if ports != 2 {
 			t.Errorf("expected ports = 2, got '%d'", ports)
 		}
+		replicas := obj.Spec.Replicas
+		if int(*replicas) != 1 {
+			t.Errorf("expected replicas = 1, got '%d'", replicas)
+		}
 		return true, obj, nil
 	})
 	fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) {
@@ -236,6 +240,29 @@ func TestInstall(t *testing.T) {
 	}
 }
 
+func TestInstallHA(t *testing.T) {
+	image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
+
+	fc := &fake.Clientset{}
+	fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
+		obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment)
+		replicas := obj.Spec.Replicas
+		if int(*replicas) != 2 {
+			t.Errorf("expected replicas = 2, got '%d'", replicas)
+		}
+		return true, obj, nil
+	})
+
+	opts := &Options{
+		Namespace: v1.NamespaceDefault,
+		ImageSpec: image,
+		Replicas:  2,
+	}
+	if err := Install(fc, opts); err != nil {
+		t.Errorf("unexpected error: %#+v", err)
+	}
+}
+
 func TestInstall_WithTLS(t *testing.T) {
 	image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
 	name := "tiller-secret"
diff --git a/cmd/helm/installer/options.go b/cmd/helm/installer/options.go
index edff2740f..13cf43dcc 100644
--- a/cmd/helm/installer/options.go
+++ b/cmd/helm/installer/options.go
@@ -81,6 +81,11 @@ type Options struct {
 	// Less than or equal to zero means no limit.
 	MaxHistory int
 
+	// Replicas sets the amount of Tiller replicas to start
+	//
+	// Less than or equals to 1 means 1.
+	Replicas int
+
 	// NodeSelectors determine which nodes Tiller can land on.
 	NodeSelectors string
 
@@ -109,6 +114,14 @@ func (opts *Options) pullPolicy() v1.PullPolicy {
 	return v1.PullIfNotPresent
 }
 
+func (opts *Options) getReplicas() *int32 {
+	replicas := int32(1)
+	if opts.Replicas > 1 {
+		replicas = int32(opts.Replicas)
+	}
+	return &replicas
+}
+
 func (opts *Options) tls() bool { return opts.EnableTLS || opts.VerifyTLS }
 
 // valuesMap returns user set values in map format
diff --git a/docs/helm/helm_init.md b/docs/helm/helm_init.md
index 856e9b565..5ed24e60d 100644
--- a/docs/helm/helm_init.md
+++ b/docs/helm/helm_init.md
@@ -43,6 +43,7 @@ helm init
       --node-selectors string    labels to specify the node on which Tiller is installed (app=tiller,helm=rocks)
   -o, --output OutputFormat      skip installation and output Tiller's manifest in specified format (json or yaml)
       --override stringArray     override values for the Tiller Deployment manifest (can specify multiple or separate values with commas: key1=val1,key2=val2)
+      --replicas int             amount of tiller instances to run on the cluster (default 1)
       --service-account string   name of service account
       --skip-refresh             do not refresh (download) the local repository cache
       --stable-repo-url string   URL for stable repository (default "https://kubernetes-charts.storage.googleapis.com")
@@ -69,4 +70,4 @@ helm init
 ### SEE ALSO
 * [helm](helm.md)	 - The Helm package manager for Kubernetes.
 
-###### Auto generated by spf13/cobra on 25-Jan-2018
+###### Auto generated by spf13/cobra on 6-Mar-2018
-- 
GitLab