From 9e1dbeaa483b6887e81e3194dd3ed477bf6ad64b Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 6 Jan 2016 15:19:45 -0800 Subject: [PATCH] feat(*): stub out install --- cmd/helm.go | 5 +- cmd/install.go | 17 ++++ dm/install.go | 171 ++++++++++++++++++++++++++++++++++++++++ format/messages.go | 22 ++++++ glide.yaml | 2 +- kubectl/cluster_info.go | 12 +++ kubectl/command.go | 32 ++++++++ kubectl/create.go | 29 +++++++ kubectl/create_test.go | 22 ++++++ kubectl/delete.go | 25 ++++++ kubectl/get.go | 27 +++++++ kubectl/get_test.go | 17 ++++ kubectl/kubectl.go | 25 ++++++ kubectl/kubectl_test.go | 12 +++ 14 files changed, 416 insertions(+), 2 deletions(-) create mode 100644 cmd/install.go create mode 100644 dm/install.go create mode 100644 format/messages.go create mode 100644 kubectl/cluster_info.go create mode 100644 kubectl/command.go create mode 100644 kubectl/create.go create mode 100644 kubectl/create_test.go create mode 100644 kubectl/delete.go create mode 100644 kubectl/get.go create mode 100644 kubectl/get_test.go create mode 100644 kubectl/kubectl.go create mode 100644 kubectl/kubectl_test.go diff --git a/cmd/helm.go b/cmd/helm.go index 6e79f2b18..36d08d642 100644 --- a/cmd/helm.go +++ b/cmd/helm.go @@ -21,7 +21,10 @@ func main() { func commands() []cli.Command { return []cli.Command{ { - Name: "install", + Name: "install", + Usage: "Initialize the client and install DM on Kubernetes.", + Description: ``, + Action: func(c *cli.Context) { install() }, }, { Name: "target", diff --git a/cmd/install.go b/cmd/install.go new file mode 100644 index 000000000..5abd44b2a --- /dev/null +++ b/cmd/install.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/deis/helm-dm/dm" + "github.com/deis/helm-dm/format" + "github.com/deis/helm-dm/kubectl" +) + +func install() error { + runner := &kubectl.PrintRunner{} + out, err := dm.Install(runner) + if err != nil { + format.Error("Error installing: %s %s", out, err) + } + format.Msg(out) + return nil +} diff --git a/dm/install.go b/dm/install.go new file mode 100644 index 000000000..65899bb61 --- /dev/null +++ b/dm/install.go @@ -0,0 +1,171 @@ +package dm + +import ( + "github.com/deis/helm-dm/kubectl" +) + +// Install uses kubectl to install the base DM. +// +// Returns the string output received from the operation, and an error if the +// command failed. +func Install(runner kubectl.Runner) (string, error) { + o, err := runner.Create([]byte(InstallYAML), "dm") + return string(o), err +} + +// InstallYAML is the installation YAML for DM. +const InstallYAML = ` +###################################################################### +# Copyright 2015 The Kubernetes Authors All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +###################################################################### + +--- +apiVersion: v1 +kind: Namespace +metadata: + labels: + app: dm + name: dm-namespace + name: dm +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: dm + name: expandybird-service + name: expandybird-service + namespace: dm +spec: + ports: + - name: expandybird + port: 8081 + targetPort: 8080 + selector: + app: dm + name: expandybird +--- +apiVersion: v1 +kind: ReplicationController +metadata: + labels: + app: dm + name: expandybird-rc + name: expandybird-rc + namespace: dm +spec: + replicas: 2 + selector: + app: dm + name: expandybird + template: + metadata: + labels: + app: dm + name: expandybird + spec: + containers: + - env: [] + image: gcr.io/dm-k8s-testing/expandybird:latest + name: expandybird + ports: + - containerPort: 8080 + name: expandybird +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: dm + name: resourcifier-service + name: resourcifier-service + namespace: dm +spec: + ports: + - name: resourcifier + port: 8082 + targetPort: 8080 + selector: + app: dm + name: resourcifier +--- +apiVersion: v1 +kind: ReplicationController +metadata: + labels: + app: dm + name: resourcifier-rc + name: resourcifier-rc + namespace: dm +spec: + replicas: 2 + selector: + app: dm + name: resourcifier + template: + metadata: + labels: + app: dm + name: resourcifier + spec: + containers: + - env: [] + image: gcr.io/dm-k8s-testing/resourcifier:latest + name: resourcifier + ports: + - containerPort: 8080 + name: resourcifier +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: dm + name: manager-service + name: manager-service + namespace: dm +spec: + ports: + - name: manager + port: 8080 + targetPort: 8080 + selector: + app: dm + name: manager +--- +apiVersion: v1 +kind: ReplicationController +metadata: + labels: + app: dm + name: manager-rc + name: manager-rc + namespace: dm +spec: + replicas: 1 + selector: + app: dm + name: manager + template: + metadata: + labels: + app: dm + name: manager + spec: + containers: + - env: [] + image: gcr.io/dm-k8s-testing/manager:latest + name: manager + ports: + - containerPort: 8080 + name: manager +` diff --git a/format/messages.go b/format/messages.go new file mode 100644 index 000000000..0eec683e1 --- /dev/null +++ b/format/messages.go @@ -0,0 +1,22 @@ +package format + +import ( + "fmt" + "os" +) + +// This is all just placeholder. + +func Error(msg string, v ...interface{}) { + msg = "[ERROR]" + msg + "\n" + fmt.Fprintf(os.Stderr, msg, v...) +} + +func Info(msg string, v ...interface{}) { + msg = "[INFO]" + msg + "\n" + fmt.Fprintf(os.Stdout, msg, v...) +} + +func Msg(msg string, v ...interface{}) { + fmt.Fprintf(os.Stdout, msg, v...) +} diff --git a/glide.yaml b/glide.yaml index bd597f0ec..35922b0fc 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,4 +1,4 @@ -package: github.com/engineyard/helm-dm +package: github.com/deis/helm-dm import: - package: github.com/codegangsta/cli - package: github.com/kubernetes/deployment-manager diff --git a/kubectl/cluster_info.go b/kubectl/cluster_info.go new file mode 100644 index 000000000..9449319c0 --- /dev/null +++ b/kubectl/cluster_info.go @@ -0,0 +1,12 @@ +package kubectl + +// ClusterInfo returns Kubernetes cluster info +func (r RealRunner) ClusterInfo() ([]byte, error) { + return command("cluster-info").CombinedOutput() +} + +// ClusterInfo returns the commands to kubectl +func (r PrintRunner) ClusterInfo() ([]byte, error) { + cmd := command("cluster-info") + return []byte(cmd.String()), nil +} diff --git a/kubectl/command.go b/kubectl/command.go new file mode 100644 index 000000000..b36e0ad33 --- /dev/null +++ b/kubectl/command.go @@ -0,0 +1,32 @@ +package kubectl + +import ( + "bytes" + "fmt" + "io/ioutil" + "os/exec" + "strings" +) + +type cmd struct { + *exec.Cmd +} + +func command(args ...string) *cmd { + return &cmd{exec.Command(Path, args...)} +} + +func assignStdin(cmd *cmd, in []byte) { + cmd.Stdin = bytes.NewBuffer(in) +} + +func (c *cmd) String() string { + var stdin string + + if c.Stdin != nil { + b, _ := ioutil.ReadAll(c.Stdin) + stdin = fmt.Sprintf("< %s", string(b)) + } + + return fmt.Sprintf("[CMD] %s %s", strings.Join(c.Args, " "), stdin) +} diff --git a/kubectl/create.go b/kubectl/create.go new file mode 100644 index 000000000..bd903b740 --- /dev/null +++ b/kubectl/create.go @@ -0,0 +1,29 @@ +package kubectl + +// Create uploads a chart to Kubernetes +func (r RealRunner) Create(stdin []byte, ns string) ([]byte, error) { + args := []string{"create", "-f", "-"} + + if ns != "" { + args = append([]string{"--namespace=" + ns}, args...) + } + + cmd := command(args...) + assignStdin(cmd, stdin) + + return cmd.CombinedOutput() +} + +// Create returns the commands to kubectl +func (r PrintRunner) Create(stdin []byte, ns string) ([]byte, error) { + args := []string{"create", "-f", "-"} + + if ns != "" { + args = append([]string{"--namespace=" + ns}, args...) + } + + cmd := command(args...) + assignStdin(cmd, stdin) + + return []byte(cmd.String()), nil +} diff --git a/kubectl/create_test.go b/kubectl/create_test.go new file mode 100644 index 000000000..0623769d7 --- /dev/null +++ b/kubectl/create_test.go @@ -0,0 +1,22 @@ +package kubectl + +import ( + "testing" +) + +func TestPrintCreate(t *testing.T) { + var client Runner = PrintRunner{} + + expected := `[CMD] kubectl --namespace=default-namespace create -f - < some stdin data` + + out, err := client.Create([]byte("some stdin data"), "default-namespace") + if err != nil { + t.Error(err) + } + + actual := string(out) + + if expected != actual { + t.Fatalf("actual %s != expected %s", actual, expected) + } +} diff --git a/kubectl/delete.go b/kubectl/delete.go new file mode 100644 index 000000000..903442585 --- /dev/null +++ b/kubectl/delete.go @@ -0,0 +1,25 @@ +package kubectl + +// Delete removes a chart from Kubernetes. +func (r RealRunner) Delete(name, ktype, ns string) ([]byte, error) { + + args := []string{"delete", ktype, name} + + if ns != "" { + args = append([]string{"--namespace=" + ns}, args...) + } + return command(args...).CombinedOutput() +} + +// Delete returns the commands to kubectl +func (r PrintRunner) Delete(name, ktype, ns string) ([]byte, error) { + + args := []string{"delete", ktype, name} + + if ns != "" { + args = append([]string{"--namespace=" + ns}, args...) + } + + cmd := command(args...) + return []byte(cmd.String()), nil +} diff --git a/kubectl/get.go b/kubectl/get.go new file mode 100644 index 000000000..bacef13d5 --- /dev/null +++ b/kubectl/get.go @@ -0,0 +1,27 @@ +package kubectl + +// Get returns Kubernetes resources +func (r RealRunner) Get(stdin []byte, ns string) ([]byte, error) { + args := []string{"get", "-f", "-"} + + if ns != "" { + args = append([]string{"--namespace=" + ns}, args...) + } + cmd := command(args...) + assignStdin(cmd, stdin) + + return cmd.CombinedOutput() +} + +// Get returns the commands to kubectl +func (r PrintRunner) Get(stdin []byte, ns string) ([]byte, error) { + args := []string{"get", "-f", "-"} + + if ns != "" { + args = append([]string{"--namespace=" + ns}, args...) + } + cmd := command(args...) + assignStdin(cmd, stdin) + + return []byte(cmd.String()), nil +} diff --git a/kubectl/get_test.go b/kubectl/get_test.go new file mode 100644 index 000000000..532724a20 --- /dev/null +++ b/kubectl/get_test.go @@ -0,0 +1,17 @@ +package kubectl + +import ( + "testing" +) + +func TestGet(t *testing.T) { + Client = TestRunner{ + out: []byte("running the get command"), + } + + expects := "running the get command" + out, _ := Client.Get([]byte{}, "") + if string(out) != expects { + t.Errorf("%s != %s", string(out), expects) + } +} diff --git a/kubectl/kubectl.go b/kubectl/kubectl.go new file mode 100644 index 000000000..466944398 --- /dev/null +++ b/kubectl/kubectl.go @@ -0,0 +1,25 @@ +package kubectl + +// Path is the path of the kubectl binary +var Path = "kubectl" + +// Runner is an interface to wrap kubectl convenience methods +type Runner interface { + // ClusterInfo returns Kubernetes cluster info + ClusterInfo() ([]byte, error) + // Create uploads a chart to Kubernetes + Create([]byte, string) ([]byte, error) + // Delete removes a chart from Kubernetes. + Delete(string, string, string) ([]byte, error) + // Get returns Kubernetes resources + Get([]byte, string) ([]byte, error) +} + +// RealRunner implements Runner to execute kubectl commands +type RealRunner struct{} + +// PrintRunner implements Runner to return a []byte of the command to be executed +type PrintRunner struct{} + +// Client stores the instance of Runner +var Client Runner = RealRunner{} diff --git a/kubectl/kubectl_test.go b/kubectl/kubectl_test.go new file mode 100644 index 000000000..453667330 --- /dev/null +++ b/kubectl/kubectl_test.go @@ -0,0 +1,12 @@ +package kubectl + +type TestRunner struct { + Runner + + out []byte + err error +} + +func (r TestRunner) Get(stdin []byte, ns string) ([]byte, error) { + return r.out, r.err +}