WIP feat(helm): Add --node-selectors and --output flags to helm init

This feature enables users to specify more control over where Tiller pod
lands by allowing "node-selectors" to be specified. Alternatively, the
"--output" flag will skip install and dump Tiller's raw Deployment manifest
 to stdout so user may alter it as they see fit (probably with a JSON manipulation
  tool like jq).

Closes #2221
reviewable/pr2557/r1
Justin Scott 8 years ago
parent 72cd1464d2
commit ab99ac5e3c

@ -26,6 +26,7 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/helm/cmd/helm/installer" "k8s.io/helm/cmd/helm/installer"
"k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/getter"
"k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/helm/helmpath"
@ -120,6 +121,9 @@ func newInitCmd(out io.Writer) *cobra.Command {
f.BoolVar(&i.opts.EnableHostNetwork, "net-host", false, "install tiller with net=host") f.BoolVar(&i.opts.EnableHostNetwork, "net-host", false, "install tiller with net=host")
f.StringVar(&i.serviceAccount, "service-account", "", "name of service account") f.StringVar(&i.serviceAccount, "service-account", "", "name of service account")
f.StringVar(&i.opts.NodeSelectors, "node-selectors", "", "labels to select which node tiller lands on")
f.StringVarP(&i.opts.Output, "output", "o", "", "skip installation and output tiller's manifest in specified format")
return cmd return cmd
} }
@ -159,31 +163,52 @@ func (i *initCmd) run() error {
i.opts.ImageSpec = i.image i.opts.ImageSpec = i.image
i.opts.ServiceAccount = i.serviceAccount i.opts.ServiceAccount = i.serviceAccount
if settings.Debug { writeYAMLManifest := func(apiVersion, kind, body string, first, last bool) error {
writeYAMLManifest := func(apiVersion, kind, body string, first, last bool) error { w := i.out
w := i.out if !first {
if !first { // YAML starting document boundary marker
// YAML starting document boundary marker if _, err := fmt.Fprintln(w, "---"); err != nil {
if _, err := fmt.Fprintln(w, "---"); err != nil {
return err
}
}
if _, err := fmt.Fprintln(w, "apiVersion:", apiVersion); err != nil {
return err return err
} }
if _, err := fmt.Fprintln(w, "kind:", kind); err != nil { }
if _, err := fmt.Fprintln(w, "apiVersion:", apiVersion); err != nil {
return err
}
if _, err := fmt.Fprintln(w, "kind:", kind); err != nil {
return err
}
if _, err := fmt.Fprint(w, body); err != nil {
return err
}
if !last {
return nil
}
// YAML ending document boundary marker
_, err := fmt.Fprintln(w, "...")
return err
}
if len(i.opts.Output) > 0 {
switch i.opts.Output {
case "json":
var body string
var err error
if body, err = installer.DeploymentManifest(&i.opts); err != nil {
return err return err
} }
if _, err := fmt.Fprint(w, body); err != nil { jsonb, err := yaml.ToJSON([]byte(body))
if err != nil {
return err return err
} }
if !last { jsons := string(jsonb)
return nil jsons = "{\"apiVersion\":\"extensions/v1beta1\",\"kind\":\"Deployment\"," + jsons[1:]
} fmt.Fprint(i.out, jsons)
// YAML ending document boundary marker
_, err := fmt.Fprintln(w, "...") return nil
return err default:
return fmt.Errorf("Unknown output format: %s", i.opts.Output)
} }
}
if settings.Debug {
var body string var body string
var err error var err error

@ -19,6 +19,8 @@ package installer // import "k8s.io/helm/cmd/helm/installer"
import ( import (
"io/ioutil" "io/ioutil"
"strings"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -116,8 +118,26 @@ func generateLabels(labels map[string]string) map[string]string {
return labels return labels
} }
// parseNodeSelectors takes a comma delimited list of key=values pairs and returns a map
func parseNodeSelectors(labels string) map[string]string {
kv := strings.Split(labels, ",")
nodeSelectors := map[string]string{}
for _, v := range kv {
el := strings.Split(v, "=")
if len(el) == 2 {
nodeSelectors[el[0]] = el[1]
}
}
return nodeSelectors
}
func generateDeployment(opts *Options) *v1beta1.Deployment { func generateDeployment(opts *Options) *v1beta1.Deployment {
labels := generateLabels(map[string]string{"name": "tiller"}) labels := generateLabels(map[string]string{"name": "tiller"})
nodeSelectors := map[string]string{}
if len(opts.NodeSelectors) > 0 {
nodeSelectors = parseNodeSelectors(opts.NodeSelectors)
}
d := &v1beta1.Deployment{ d := &v1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Namespace: opts.Namespace, Namespace: opts.Namespace,
@ -164,10 +184,8 @@ func generateDeployment(opts *Options) *v1beta1.Deployment {
}, },
}, },
}, },
HostNetwork: opts.EnableHostNetwork, HostNetwork: opts.EnableHostNetwork,
NodeSelector: map[string]string{ NodeSelector: nodeSelectors,
"beta.kubernetes.io/os": "linux",
},
}, },
}, },
}, },
@ -203,6 +221,7 @@ func generateDeployment(opts *Options) *v1beta1.Deployment {
}, },
}) })
} }
return d return d
} }

@ -71,6 +71,12 @@ type Options struct {
// EnableHostNetwork installs Tiller with net=host // EnableHostNetwork installs Tiller with net=host
EnableHostNetwork bool EnableHostNetwork bool
// NodeSelectors determine which nodes Tiller can land on
NodeSelectors string
// Output dumps the Tiller manifest in the specified format (e.g. json) but skips Helm/Tiller installation
Output string
} }
func (opts *Options) selectImage() string { func (opts *Options) selectImage() string {

Loading…
Cancel
Save