From 47398de71c7d007549509551e7d5758d553c6536 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 21 Jun 2016 11:25:12 -0700 Subject: [PATCH 1/3] fix(tunnel): allow tunneling to non-default namespace --- cmd/helm/helm.go | 11 +++++++---- cmd/helm/init.go | 2 -- cmd/helm/tunnel.go | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 0fe1cd589..2b505905b 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -17,8 +17,11 @@ const ( hostEnvVar = "HELM_HOST" ) -var helmHome string -var tillerHost string +var ( + helmHome string + tillerHost string + tillerNamespace string +) // flagDebug is a signal that the user wants additional output. var flagDebug bool @@ -61,6 +64,7 @@ func init() { p := RootCommand.PersistentFlags() p.StringVar(&helmHome, "home", home, "location of your Helm config. Overrides $HELM_HOME.") p.StringVar(&tillerHost, "host", thost, "address of tiller. Overrides $HELM_HOST.") + p.StringVarP(&tillerNamespace, "namespace", "n", "", "kubernetes namespace") p.BoolVarP(&flagDebug, "debug", "", false, "enable verbose output") } @@ -72,8 +76,7 @@ func main() { func setupConnection(c *cobra.Command, args []string) error { if tillerHost == "" { - // Should failure fall back to default host? - tunnel, err := newTillerPortForwarder() + tunnel, err := newTillerPortForwarder(tillerNamespace) if err != nil { return err } diff --git a/cmd/helm/init.go b/cmd/helm/init.go index 90db88eaa..c3f263e7f 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -17,7 +17,6 @@ Kubernetes Cluster and sets up local configuration in $HELM_HOME (default: ~/.he var ( tillerImg string - tillerNamespace string clientOnly bool initSkipNamespace bool defaultRepository = "kubernetes-charts" @@ -29,7 +28,6 @@ func init() { f.StringVarP(&tillerImg, "tiller-image", "i", "", "override tiller image") f.BoolVarP(&clientOnly, "client-only", "c", false, "If set does not install tiller") f.BoolVarP(&initSkipNamespace, "skip-namespace", "s", false, "Do not attempt to create a namespace. Assume the namespace is already there.") - f.StringVarP(&tillerNamespace, "namespace", "n", "helm", "set the tiller namespace") RootCommand.AddCommand(initCmd) } diff --git a/cmd/helm/tunnel.go b/cmd/helm/tunnel.go index 9b65fdd0c..abf7fe401 100644 --- a/cmd/helm/tunnel.go +++ b/cmd/helm/tunnel.go @@ -12,14 +12,14 @@ import ( // TODO refactor out this global var var tunnel *kube.Tunnel -func newTillerPortForwarder() (*kube.Tunnel, error) { - podName, err := getTillerPodName("helm") +func newTillerPortForwarder(namespace string) (*kube.Tunnel, error) { + podName, err := getTillerPodName(namespace) if err != nil { return nil, err } // FIXME use a constain that is accessible on init const tillerPort = 44134 - return kube.New(nil).ForwardPort("helm", podName, tillerPort) + return kube.New(nil).ForwardPort(namespace, podName, tillerPort) } func getTillerPodName(namespace string) (string, error) { From 894cdb03bddfe580291f44d76d254da44b737d3c Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 21 Jun 2016 11:26:36 -0700 Subject: [PATCH 2/3] fix(cli): resolve flag name conflict --- cmd/helm/helm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 2b505905b..efa5ab589 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -64,7 +64,7 @@ func init() { p := RootCommand.PersistentFlags() p.StringVar(&helmHome, "home", home, "location of your Helm config. Overrides $HELM_HOME.") p.StringVar(&tillerHost, "host", thost, "address of tiller. Overrides $HELM_HOST.") - p.StringVarP(&tillerNamespace, "namespace", "n", "", "kubernetes namespace") + p.StringVarP(&tillerNamespace, "namespace", "", "", "kubernetes namespace") p.BoolVarP(&flagDebug, "debug", "", false, "enable verbose output") } From 573e5aa7fc00da16c3a443d9e2046c5be6140511 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 21 Jun 2016 11:28:03 -0700 Subject: [PATCH 3/3] feat(kube): create namespace if it does not exist --- cmd/helm/init.go | 9 +----- pkg/client/install.go | 64 ++++++++++++------------------------------- pkg/kube/client.go | 22 ++++++++++++++- 3 files changed, 40 insertions(+), 55 deletions(-) diff --git a/cmd/helm/init.go b/cmd/helm/init.go index c3f263e7f..a9418dc23 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -18,7 +18,6 @@ Kubernetes Cluster and sets up local configuration in $HELM_HOME (default: ~/.he var ( tillerImg string clientOnly bool - initSkipNamespace bool defaultRepository = "kubernetes-charts" defaultRepositoryURL = "http://storage.googleapis.com/kubernetes-charts" ) @@ -27,7 +26,6 @@ func init() { f := initCmd.Flags() f.StringVarP(&tillerImg, "tiller-image", "i", "", "override tiller image") f.BoolVarP(&clientOnly, "client-only", "c", false, "If set does not install tiller") - f.BoolVarP(&initSkipNamespace, "skip-namespace", "s", false, "Do not attempt to create a namespace. Assume the namespace is already there.") RootCommand.AddCommand(initCmd) } @@ -61,12 +59,7 @@ func runInit(cmd *cobra.Command, args []string) error { } func installTiller() error { - i := client.NewInstaller() - i.Tiller["Image"] = tillerImg - i.Tiller["Namespace"] = tillerNamespace - err := i.Install(flagDebug, !initSkipNamespace) - - if err != nil { + if err := client.Install(tillerNamespace, tillerImg, flagDebug); err != nil { return fmt.Errorf("error installing: %s", err) } fmt.Println("\nTiller (the helm server side component) has been installed into your Kubernetes Cluster.") diff --git a/pkg/client/install.go b/pkg/client/install.go index 1ca9d257a..cf948ae4b 100644 --- a/pkg/client/install.go +++ b/pkg/client/install.go @@ -10,49 +10,33 @@ import ( "k8s.io/helm/pkg/kube" ) -// Installer installs tiller into Kubernetes -// -// See InstallYAML. -type Installer struct { - - // Metadata holds any global metadata attributes for the resources - Metadata map[string]interface{} - - // Tiller specific metadata - Tiller map[string]interface{} -} - -// NewInstaller creates a new Installer -func NewInstaller() *Installer { - return &Installer{ - Metadata: map[string]interface{}{}, - Tiller: map[string]interface{}{}, - } -} - // Install uses kubernetes client to install tiller // // Returns the string output received from the operation, and an error if the // command failed. // // If verbose is true, this will print the manifest to stdout. -// -// If createNS is true, this will also create the namespace. -func (i *Installer) Install(verbose, createNS bool) error { - - var b bytes.Buffer +func Install(namespace, image string, verbose bool) error { + kc := kube.New(nil) - // Add namespace - if createNS { - nstpl := template.New("namespace").Funcs(sprig.TxtFuncMap()) - if err := template.Must(nstpl.Parse(NamespaceYAML)).Execute(&b, i); err != nil { + if namespace == "" { + ns, _, err := kc.DefaultNamespace() + if err != nil { return err } + namespace = ns } + var b bytes.Buffer + // Add main install YAML istpl := template.New("install").Funcs(sprig.TxtFuncMap()) - if err := template.Must(istpl.Parse(InstallYAML)).Execute(&b, i); err != nil { + + cfg := struct { + Namespace, Image string + }{namespace, image} + + if err := template.Must(istpl.Parse(InstallYAML)).Execute(&b, cfg); err != nil { return err } @@ -60,24 +44,12 @@ func (i *Installer) Install(verbose, createNS bool) error { fmt.Println(b.String()) } - return kube.New(nil).Create(i.Tiller["Namespace"].(string), &b) + return kc.Create(namespace, &b) } -// NamespaceYAML is the installation for a namespace. -const NamespaceYAML = ` ----{{$namespace := default "helm" .Tiller.Namespace}} -apiVersion: v1 -kind: Namespace -metadata: - labels: - app: helm - name: helm-namespace - name: {{$namespace}} -` - // InstallYAML is the installation YAML for DM. const InstallYAML = ` ----{{$namespace := default "helm" .Tiller.Namespace}} +--- apiVersion: v1 kind: ReplicationController metadata: @@ -85,7 +57,7 @@ metadata: app: helm name: tiller name: tiller-rc - namespace: {{$namespace}} + namespace: {{ .Namespace }} spec: replicas: 1 selector: @@ -103,7 +75,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: {{default "gcr.io/kubernetes-helm/tiller:canary" .Tiller.Image}} + image: {{default "gcr.io/kubernetes-helm/tiller:canary" .Image}} name: tiller ports: - containerPort: 44134 diff --git a/pkg/kube/client.go b/pkg/kube/client.go index b6a22e9ec..07060e222 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -4,6 +4,8 @@ import ( "fmt" "io" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" @@ -28,6 +30,9 @@ type ResourceActorFunc func(*resource.Info) error // // Namespace will set the namespace func (c *Client) Create(namespace string, reader io.Reader) error { + if err := c.ensureNamespace(namespace); err != nil { + return err + } return perform(c, namespace, reader, createResource) } @@ -44,7 +49,7 @@ func perform(c *Client, namespace string, reader io.Reader, fn ResourceActorFunc r := c.NewBuilder(includeThirdPartyAPIs). ContinueOnError(). NamespaceParam(namespace). - RequireNamespace(). + DefaultNamespace(). Stream(reader, ""). Flatten(). Do() @@ -83,3 +88,18 @@ func createResource(info *resource.Info) error { func deleteResource(info *resource.Info) error { return resource.NewHelper(info.Client, info.Mapping).Delete(info.Namespace, info.Name) } + +func (c *Client) ensureNamespace(namespace string) error { + client, err := c.Client() + if err != nil { + return err + } + + ns := &api.Namespace{} + ns.Name = namespace + _, err = client.Namespaces().Create(ns) + if err != nil && !errors.IsAlreadyExists(err) { + return err + } + return nil +}