Merge pull request #25 from technosophos/feat/deploy

Feat/deploy
pull/291/head
Matt Butcher 10 years ago
commit d8eda34998

@ -1,7 +1,7 @@
language: go language: go
env: env:
- GO15VENDOREXPERIMENT=1 - GO15VENDOREXPERIMENT=1 GLIDE_VERSION="0.9.1"
branches: branches:
only: only:
@ -16,9 +16,9 @@ go:
- 1.5 - 1.5
install: install:
- wget "https://github.com/Masterminds/glide/releases/download/0.8.3/glide-0.8.3-linux-amd64.tar.gz" - wget "https://github.com/Masterminds/glide/releases/download/$GLIDE_VERSION/glide-$GLIDE_VERSION-linux-amd64.tar.gz"
- mkdir -p $HOME/bin - mkdir -p $HOME/bin
- tar -vxz -C $HOME/bin --strip=1 -f glide-0.8.3-linux-amd64.tar.gz - tar -vxz -C $HOME/bin --strip=1 -f glide-$GLIDE_VERSION-linux-amd64.tar.gz
- export PATH="$HOME/bin:$PATH" GLIDE_HOME="$HOME/.glide" - export PATH="$HOME/bin:$PATH" GLIDE_HOME="$HOME/.glide"
script: make bootstrap build test script: make bootstrap build test

@ -4,7 +4,9 @@ endif
BIN_DIR := bin BIN_DIR := bin
DIST_DIR := _dist DIST_DIR := _dist
GO_PACKAGES := cmd/helm dm deploy format kubectl #GO_PACKAGES := cmd/helm dm format kubectl
GO_DIRS ?= $(shell glide nv -x )
GO_PKGS ?= $(shell glide nv)
MAIN_GO := github.com/deis/helm-dm/cmd/helm MAIN_GO := github.com/deis/helm-dm/cmd/helm
HELM_BIN := helm-dm HELM_BIN := helm-dm
PATH_WITH_HELM = PATH="$(shell pwd)/$(BIN_DIR)/helm:$(PATH)" PATH_WITH_HELM = PATH="$(shell pwd)/$(BIN_DIR)/helm:$(PATH)"
@ -44,19 +46,19 @@ install: build
install -m 755 bin/${HELM_BIN} ${DESTDIR}/usr/local/bin/${HELM_BIN} install -m 755 bin/${HELM_BIN} ${DESTDIR}/usr/local/bin/${HELM_BIN}
quicktest: quicktest:
$(PATH_WITH_HELM) go test -short $(addprefix ./,$(GO_PACKAGES)) $(PATH_WITH_HELM) go test -short ${GO_PKGS}
test: test-style test: test-style
$(PATH_WITH_HELM) go test -v -cover $(addprefix ./,$(GO_PACKAGES)) $(PATH_WITH_HELM) go test -v -cover ${GO_PKGS}
test-style: test-style:
@if [ $(shell gofmt -e -l -s $(GO_PACKAGES)) ]; then \ @if [ $(shell gofmt -e -l -s $(GO_DIRS)) ]; then \
echo "gofmt check failed:"; gofmt -e -d -s $(GO_PACKAGES); exit 1; \ echo "gofmt check failed:"; gofmt -e -d -s $(GO_DIRS); exit 1; \
fi fi
@for i in . $(GO_PACKAGES); do \ @for i in . $(GO_DIRS); do \
golint $$i; \ golint $$i; \
done done
@for i in . $(GO_PACKAGES); do \ @for i in . $(GO_DIRS); do \
go vet github.com/deis/helm-dm/$$i; \ go vet github.com/deis/helm-dm/$$i; \
done done

@ -0,0 +1,99 @@
package main
import (
"errors"
"fmt"
"os"
"regexp"
"strings"
"github.com/aokoli/goutils"
"github.com/codegangsta/cli"
"github.com/deis/helm-dm/format"
"github.com/kubernetes/deployment-manager/chart"
)
func uploadChart(c *cli.Context) error {
args := c.Args()
if len(args) < 1 {
format.Err("First argument, filename, is required. Try 'helm deploy --help'")
os.Exit(1)
}
cname := c.String("name")
fname := args[0]
if fname == "" {
return errors.New("A filename must be specified. For a tar archive, this is the name of the root template in the archive.")
}
_, err := doUpload(fname, cname, c)
return err
}
func doUpload(filename, cname string, cxt *cli.Context) (string, error) {
fi, err := os.Stat(filename)
if err != nil {
return "", err
}
if fi.IsDir() {
format.Info("Chart is directory")
c, err := chart.LoadDir(filename)
if err != nil {
return "", err
}
if cname == "" {
cname = genName(c.Chartfile().Name)
}
// TODO: Is it better to generate the file in temp dir like this, or
// just put it in the CWD?
//tdir, err := ioutil.TempDir("", "helm-")
//if err != nil {
//format.Warn("Could not create temporary directory. Using .")
//tdir = "."
//} else {
//defer os.RemoveAll(tdir)
//}
tdir := "."
tfile, err := chart.Save(c, tdir)
if err != nil {
return "", err
}
filename = tfile
} else if cname == "" {
n, _, e := parseTarName(filename)
if e != nil {
return "", e
}
cname = n
}
// TODO: Add a version build metadata on the chart.
if cxt.Bool("dry-run") {
format.Info("Prepared deploy %q using file %q", cname, filename)
return "", nil
}
c := client(cxt)
return c.PostChart(filename, cname)
}
func genName(pname string) string {
s, _ := goutils.RandomAlphaNumeric(8)
return fmt.Sprintf("%s-%s", pname, s)
}
func parseTarName(name string) (string, string, error) {
tnregexp := regexp.MustCompile(chart.TarNameRegex)
if strings.HasSuffix(name, ".tgz") {
name = strings.TrimSuffix(name, ".tgz")
}
v := tnregexp.FindStringSubmatch(name)
if v == nil {
return name, "", fmt.Errorf("invalid name %s", name)
}
return v[1], v[2], nil
}

@ -2,6 +2,7 @@ package main
import ( import (
"io/ioutil" "io/ioutil"
"os"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/kubernetes/deployment-manager/common" "github.com/kubernetes/deployment-manager/common"
@ -60,7 +61,17 @@ func deploy(c *cli.Context) error {
// file with it. // file with it.
args := c.Args() args := c.Args()
if len(args) > 0 { if len(args) > 0 {
cfg.Resources[0].Type = args[0] cname := args[0]
if isLocalChart(cname) {
// If we get here, we need to first package then upload the chart.
loc, err := doUpload(cname, "", c)
if err != nil {
return err
}
cfg.Resources[0].Name = loc
} else {
cfg.Resources[0].Type = cname
}
} }
// Override the name if one is passed in. // Override the name if one is passed in.
@ -82,6 +93,12 @@ func deploy(c *cli.Context) error {
return client(c).PostDeployment(cfg) return client(c).PostDeployment(cfg)
} }
// isLocalChart returns true if the given path can be statted.
func isLocalChart(path string) bool {
_, err := os.Stat(path)
return err == nil
}
// loadConfig loads a file into a common.Configuration. // loadConfig loads a file into a common.Configuration.
func loadConfig(c *common.Configuration, filename string) error { func loadConfig(c *common.Configuration, filename string) error {
data, err := ioutil.ReadFile(filename) data, err := ioutil.ReadFile(filename)

@ -1,26 +0,0 @@
package deploy
import (
"os"
"github.com/kubernetes/deployment-manager/common"
)
// Deployment describes a deployment of a package.
type Deployment struct {
// Name is the Deployment name. Autogenerated if empty.
Name string
// Filename is the filename for the base deployment.
Filename string
// Imports is a list of imported files.
Imports []string
// Properties to pass into the template.
Properties map[string]interface{}
// Input is a file containing templates. It may be os.Stdin.
Input *os.File
// Repository is the location of the templates.
Repository string
// The template, typically generated by the Deployment.
Template *common.Template
}

@ -174,18 +174,21 @@ func (c *Client) ListDeployments() ([]string, error) {
return l, nil return l, nil
} }
// UploadChart sends a chart to DM for deploying. // PostChart sends a chart to DM for deploying.
func (c *Client) PostChart(filename, deployname string) error { //
// This returns the location for the new chart, typically of the form
// `helm:repo/bucket/name-version.tgz`.
func (c *Client) PostChart(filename, deployname string) (string, error) {
f, err := os.Open(filename) f, err := os.Open(filename)
if err != nil { if err != nil {
return err return "", err
} }
u, err := c.url("/v2/charts") u, err := c.url("/v2/charts")
request, err := http.NewRequest("POST", u, f) request, err := http.NewRequest("POST", u, f)
if err != nil { if err != nil {
f.Close() f.Close()
return err return "", err
} }
// There is an argument to be made for using the legacy x-octet-stream for // There is an argument to be made for using the legacy x-octet-stream for
@ -206,7 +209,7 @@ func (c *Client) PostChart(filename, deployname string) error {
response, err := client.Do(request) response, err := client.Do(request)
if err != nil { if err != nil {
return err return "", err
} }
// We only want 201 CREATED. Admittedly, we could accept 200 and 202. // We only want 201 CREATED. Admittedly, we could accept 200 and 202.
@ -214,12 +217,13 @@ func (c *Client) PostChart(filename, deployname string) error {
body, err := ioutil.ReadAll(response.Body) body, err := ioutil.ReadAll(response.Body)
response.Body.Close() response.Body.Close()
if err != nil { if err != nil {
return err return "", err
} }
return &HTTPError{StatusCode: response.StatusCode, Message: string(body), URL: request.URL} return "", &HTTPError{StatusCode: response.StatusCode, Message: string(body), URL: request.URL}
} }
return nil loc := response.Header.Get("Location")
return loc, nil
} }
// HTTPError is an error caused by an unexpected HTTP status code. // HTTPError is an error caused by an unexpected HTTP status code.
@ -260,6 +264,7 @@ func (c *Client) DeleteDeployment(name string) (*common.Deployment, error) {
return deployment, nil return deployment, nil
} }
// PostDeployment posts a deployment objec to the manager service.
func (c *Client) PostDeployment(cfg *common.Configuration) error { func (c *Client) PostDeployment(cfg *common.Configuration) error {
return c.CallService("/deployments", "POST", "post deployment", cfg, nil) return c.CallService("/deployments", "POST", "post deployment", cfg, nil)
} }

@ -134,7 +134,7 @@ func TestGetDeployment(t *testing.T) {
func TestPostDeployment(t *testing.T) { func TestPostDeployment(t *testing.T) {
cfg := &common.Configuration{ cfg := &common.Configuration{
[]*common.Resource{ Resources: []*common.Resource{
{ {
Name: "foo", Name: "foo",
Type: "helm:example.com/foo/bar", Type: "helm:example.com/foo/bar",

@ -38,6 +38,7 @@ func Warning(msg string, v ...interface{}) {
fmt.Fprintf(os.Stdout, msg, v...) fmt.Fprintf(os.Stdout, msg, v...)
} }
// YAML prints an object in YAML format.
func YAML(v interface{}) error { func YAML(v interface{}) error {
y, err := yaml.Marshal(v) y, err := yaml.Marshal(v)
if err != nil { if err != nil {

Loading…
Cancel
Save