Merge pull request #7 from deis/feat/create-pack

feat(create): add create and pack verbs.
pull/291/head
Matt Butcher 9 years ago
commit 03aff9bd47

@ -4,7 +4,7 @@ endif
BIN_DIR := bin
DIST_DIR := _dist
GO_PACKAGES := cmd/helm dm
GO_PACKAGES := cmd/helm dm deploy format kubectl
MAIN_GO := github.com/deis/helm-dm/cmd/helm
HELM_BIN := helm-dm
PATH_WITH_HELM = PATH="$(shell pwd)/$(BIN_DIR)/helm:$(PATH)"
@ -47,7 +47,7 @@ quicktest:
$(PATH_WITH_HELM) go test -short $(addprefix ./,$(GO_PACKAGES))
test: test-style
$(PATH_WITH_HELM) go test -v ./ $(addprefix ./,$(GO_PACKAGES))
$(PATH_WITH_HELM) go test -v $(addprefix ./,$(GO_PACKAGES))
test-style:
@if [ $(shell gofmt -e -l -s *.go $(GO_PACKAGES)) ]; then \

@ -0,0 +1,24 @@
package main
import (
"errors"
"github.com/codegangsta/cli"
"github.com/kubernetes/deployment-manager/chart"
)
func create(c *cli.Context) error {
args := c.Args()
if len(args) < 1 {
return errors.New("'helm create' requires a chart name as an argument")
}
cf := &chart.Chartfile{
Name: args[0],
Description: "Created by Helm",
Version: "0.1.0",
}
_, err := chart.Create(cf, ".")
return err
}

@ -1,38 +1,85 @@
package main
import (
"encoding/json"
"errors"
"os"
"github.com/codegangsta/cli"
dep "github.com/deis/helm-dm/deploy"
"github.com/deis/helm-dm/format"
"github.com/kubernetes/deployment-manager/chart"
)
func deploy(cfg *dep.Deployment, host string, dry bool) error {
func deploy(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)
}
props, err := parseProperties(c.String("properties"))
if err != nil {
format.Err("Failed to parse properties: %s", err)
os.Exit(1)
}
d := &dep.Deployment{
Name: c.String("Name"),
Properties: props,
Filename: args[0],
Imports: args[1:],
Repository: c.String("repository"),
}
if c.Bool("stdin") {
d.Input = os.Stdin
}
return doDeploy(d, c.GlobalString("host"), c.Bool("dry-run"))
}
func doDeploy(cfg *dep.Deployment, host string, dry bool) error {
if cfg.Filename == "" {
return errors.New("A filename must be specified. For a tar archive, this is the name of the root template in the archive.")
}
if err := cfg.Prepare(); err != nil {
format.Error("Failed to prepare deployment: %s", err)
fi, err := os.Stat(cfg.Filename)
if err != nil {
return err
}
// For a dry run, print the template and exit.
if dry {
format.Info("Template prepared for %s", cfg.Template.Name)
data, err := json.MarshalIndent(cfg.Template, "", "\t")
if fi.IsDir() {
format.Info("Chart is directory")
c, err := chart.LoadDir(cfg.Filename)
if err != nil {
return err
}
format.Msg(string(data))
return nil
//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
}
cfg.Filename = tfile
}
if err := cfg.Commit(host); err != nil {
format.Error("Failed to commit deployment: %s", err)
return err
if !dry {
if err := uploadTar(cfg.Filename); err != nil {
return err
}
}
return nil
}
func uploadTar(filename string) error {
return nil
}

@ -8,14 +8,15 @@ import (
"github.com/deis/helm-dm/kubectl"
)
var ErrAlreadyInstalled error = errors.New("Already Installed")
// ErrAlreadyInstalled indicates that DM is already installed.
var ErrAlreadyInstalled = errors.New("Already Installed")
func install(dryRun bool) error {
runner := getKubectlRunner(dryRun)
out, err := dm.Install(runner)
if err != nil {
format.Error("Error installing: %s %s", out, err)
format.Err("Error installing: %s %s", out, err)
}
format.Msg(out)
return nil
@ -26,7 +27,7 @@ func uninstall(dryRun bool) error {
out, err := dm.Uninstall(runner)
if err != nil {
format.Error("Error uninstalling: %s %s", out, err)
format.Err("Error uninstalling: %s %s", out, err)
}
format.Msg(out)
return nil

@ -2,6 +2,7 @@ package main
import (
"github.com/deis/helm-dm/dm"
"github.com/deis/helm-dm/format"
"github.com/deis/helm-dm/kubectl"
)

@ -4,7 +4,6 @@ import (
"os"
"github.com/codegangsta/cli"
dep "github.com/deis/helm-dm/deploy"
"github.com/deis/helm-dm/format"
)
@ -48,7 +47,7 @@ func commands() []cli.Command {
},
Action: func(c *cli.Context) {
if err := install(c.Bool("dry-run")); err != nil {
format.Error("%s (Run 'helm doctor' for more information)", err)
format.Err("%s (Run 'helm doctor' for more information)", err)
os.Exit(1)
}
},
@ -65,7 +64,7 @@ func commands() []cli.Command {
},
Action: func(c *cli.Context) {
if err := uninstall(c.Bool("dry-run")); err != nil {
format.Error("%s (Run 'helm doctor' for more information)", err)
format.Err("%s (Run 'helm doctor' for more information)", err)
os.Exit(1)
}
},
@ -74,7 +73,7 @@ func commands() []cli.Command {
Name: "status",
Usage: "Show status of DM.",
Action: func(c *cli.Context) {
format.Error("Not yet implemented")
format.Err("Not yet implemented")
os.Exit(1)
},
},
@ -84,7 +83,7 @@ func commands() []cli.Command {
ArgsUsage: "",
Action: func(c *cli.Context) {
if err := target(c.Bool("dry-run")); err != nil {
format.Error("%s (Is the cluster running?)", err)
format.Err("%s (Is the cluster running?)", err)
os.Exit(1)
}
},
@ -109,7 +108,7 @@ func commands() []cli.Command {
},
Action: func(c *cli.Context) {
if err := install(c.Bool("dry-run")); err != nil {
format.Error("%s (Run 'helm doctor' for more information)", err)
format.Err("%s (Run 'helm doctor' for more information)", err)
os.Exit(1)
}
},
@ -120,46 +119,27 @@ func commands() []cli.Command {
ArgsUsage: "",
Action: func(c *cli.Context) {
if err := doctor(); err != nil {
format.Error("%s", err)
format.Err("%s", err)
os.Exit(1)
}
},
},
{
Name: "create",
Usage: "Create a new local chart for editing.",
Action: func(c *cli.Context) { run(c, create) },
},
{
Name: "package",
Aliases: []string{"pack"},
Usage: "Given a chart directory, package it into a release.",
Action: func(c *cli.Context) { run(c, pack) },
},
{
Name: "deploy",
Aliases: []string{"install"},
Usage: "Deploy a chart into the cluster.",
Action: func(c *cli.Context) {
args := c.Args()
if len(args) < 1 {
format.Error("First argument, filename, is required. Try 'helm deploy --help'")
os.Exit(1)
}
props, err := parseProperties(c.String("properties"))
if err != nil {
format.Error("Failed to parse properties: %s", err)
os.Exit(1)
}
d := &dep.Deployment{
Name: c.String("Name"),
Properties: props,
Filename: args[0],
Imports: args[1:],
Repository: c.String("repository"),
}
if c.Bool("stdin") {
d.Input = os.Stdin
}
if err := deploy(d, c.GlobalString("host"), c.Bool("dry-run")); err != nil {
format.Error("%s (Try running 'helm doctor')", err)
os.Exit(1)
}
},
Action: func(c *cli.Context) { run(c, deploy) },
Flags: []cli.Flag{
cli.BoolFlag{
Name: "dry-run",
@ -193,3 +173,10 @@ func commands() []cli.Command {
listCmd(),
}
}
func run(c *cli.Context, f func(c *cli.Context) error) {
if err := f(c); err != nil {
os.Stderr.Write([]byte(err.Error()))
os.Exit(1)
}
}

@ -14,7 +14,7 @@ func listCmd() cli.Command {
Usage: "Lists the deployments in the cluster",
Action: func(c *cli.Context) {
if err := list(c.GlobalString("host")); err != nil {
format.Error("%s (Is the cluster running?)", err)
format.Err("%s (Is the cluster running?)", err)
os.Exit(1)
}
},

@ -0,0 +1,34 @@
package main
import (
"errors"
"fmt"
"os"
"github.com/codegangsta/cli"
"github.com/deis/helm-dm/format"
"github.com/kubernetes/deployment-manager/chart"
)
func pack(cxt *cli.Context) error {
args := cxt.Args()
if len(args) < 1 {
return errors.New("'helm package' requires a path to a chart directory as an argument")
}
dir := args[0]
if fi, err := os.Stat(dir); err != nil {
return fmt.Errorf("Could not find directory %s: %s", dir, err)
} else if !fi.IsDir() {
return fmt.Errorf("Not a directory: %s", dir)
}
c, err := chart.LoadDir(dir)
if err != nil {
return fmt.Errorf("Failed to load %s: %s", dir, err)
}
fname, err := chart.Save(c, ".")
format.Msg(fname)
return nil
}

@ -1,16 +1,17 @@
package deploy
import (
"archive/tar"
"errors"
"fmt"
//"archive/tar"
//"errors"
//"fmt"
"os"
"strings"
//"strings"
"github.com/ghodss/yaml"
//"github.com/ghodss/yaml"
"github.com/kubernetes/deployment-manager/chart"
"github.com/kubernetes/deployment-manager/common"
"github.com/kubernetes/deployment-manager/expandybird/expander"
"github.com/kubernetes/deployment-manager/registry"
//"github.com/kubernetes/deployment-manager/expandybird/expander"
//"github.com/kubernetes/deployment-manager/registry"
)
// Deployer is capable of deploying an object to a back-end.
@ -41,6 +42,8 @@ type Deployment struct {
// The template, typically generated by the Deployment.
Template *common.Template
lchart *chart.Chart
}
// Prepare loads templates and checks for client-side errors.
@ -48,26 +51,44 @@ type Deployment struct {
// This will generate the Template based on other information.
func (d *Deployment) Prepare() error {
tpl, err := d.resolveTemplate()
// Is Filename a local dir, a local file, or a remote URL?
fi, err := os.Stat(d.Filename)
if err != nil {
return err
}
// If a deployment Name is specified, set that explicitly.
if d.Name != "" {
tpl.Name = d.Name
var c *chart.Chart
if fi.IsDir() {
c, err = chart.LoadDir(d.Filename)
if err != nil {
return err
}
} else {
c, err = chart.Load(d.Filename)
if err != nil {
return err
}
}
d.Template = tpl
// Override name if we need to
// Properties
d.lchart = c
return nil
}
// Chart retrieves the chart from teh deployment.
func (d *Deployment) Chart() *chart.Chart {
return d.lchart
}
// Commit prepares the Deployment and then commits it to the remote processor.
func (d *Deployment) Commit(host string) error {
return nil
}
/*
// resolveTemplate resolves what kind of template is being loaded, and then returns the template.
func (d *Deployment) resolveTemplate() (*common.Template, error) {
// If some input has been specified, read it.
@ -179,3 +200,4 @@ func getGitRegistry(reg string) (registry.Registry, error) {
return r, nil
}
}
*/

@ -6,13 +6,15 @@ import (
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"time"
"github.com/ghodss/yaml"
)
// The default HTTP timeout
var DefaultHTTPTimeout time.Duration = time.Second * 10
var DefaultHTTPTimeout = time.Second * 10
// Client is a DM client.
type Client struct {
@ -48,7 +50,7 @@ func (c *Client) url(path string) string {
func (c *Client) CallService(path, method, action string, dest interface{}, reader io.ReadCloser) error {
u := c.url(path)
resp, err := c.callHttp(u, method, action, reader)
resp, err := c.callHTTP(u, method, action, reader)
if err != nil {
return err
}
@ -67,8 +69,8 @@ func (c *Client) CallService(path, method, action string, dest interface{}, read
return nil
}
// callHttp is a low-level primative for executing HTTP operations.
func (c *Client) callHttp(path, method, action string, reader io.ReadCloser) (string, error) {
// callHTTP is a low-level primative for executing HTTP operations.
func (c *Client) callHTTP(path, method, action string, reader io.ReadCloser) (string, error) {
request, err := http.NewRequest(method, path, reader)
request.Header.Add("Content-Type", "application/json")
@ -97,6 +99,7 @@ func (c *Client) callHttp(path, method, action string, reader io.ReadCloser) (st
return string(body), nil
}
// ListDeployments lists the deployments in DM.
func (c *Client) ListDeployments() error {
var d interface{}
if err := c.CallService("deployments", "GET", "foo", &d, nil); err != nil {
@ -106,3 +109,49 @@ func (c *Client) ListDeployments() error {
fmt.Printf("%#v\n", d)
return nil
}
// DeployChart sends a chart to DM for deploying.
func (c *Client) DeployChart(filename, deployname string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
request, err := http.NewRequest("POST", "/v2/deployments/", f)
// There is an argument to be made for using the legacy x-octet-stream for
// this. But since we control both sides, we should use the standard one.
// Also, gzip (x-compress) is usually treated as a content encoding. In this
// case it probably is not, but it makes more sense to follow the standard,
// even though we don't assume the remote server will strip it off.
request.Header.Add("Content-Type", "application/x-tar")
request.Header.Add("Content-Encoding", "gzip")
request.Header.Add("X-Deployment-Name", deployname)
request.Header.Add("X-Chart-Name", filepath.Base(filename))
client := http.Client{
Timeout: time.Duration(time.Duration(DefaultHTTPTimeout) * time.Second),
Transport: c.Transport,
}
response, err := client.Do(request)
if err != nil {
return err
}
body, err := ioutil.ReadAll(response.Body)
response.Body.Close()
if err != nil {
return err
}
// FIXME: We only want 200 OK or 204(?) CREATED
if response.StatusCode < http.StatusOK ||
response.StatusCode >= http.StatusMultipleChoices {
message := fmt.Sprintf("status code: %d status: %s : %s", response.StatusCode, response.Status, body)
return fmt.Errorf("Failed to post: %s", message)
}
return nil
}

@ -20,7 +20,7 @@ func IsInstalled(runner kubectl.Runner) bool {
// we know that we have both the namespace and the manager API server.
out, err := runner.GetByKind("rc", "manager-rc", "dm")
if err != nil {
format.Error("Installation not found: %s %s", out, err)
format.Err("Installation not found: %s %s", out, err)
return false
}
return true

@ -15,6 +15,7 @@ type debugTransport struct {
http.RoundTripper
}
// NewDebugTransport returns a debugging implementation of a RoundTripper.
func NewDebugTransport(rt http.RoundTripper) http.RoundTripper {
return debugTransport{
RoundTripper: rt,

@ -4,7 +4,7 @@ import (
"github.com/deis/helm-dm/kubectl"
)
// uninstall uses kubectl to uninstall the base DM.
// Uninstall uses kubectl to uninstall the base DM.
//
// Returns the string output received from the operation, and an error if the
// command failed.

@ -7,25 +7,30 @@ import (
// This is all just placeholder.
func Error(msg string, v ...interface{}) {
// Err prints an error message to Stderr.
func Err(msg string, v ...interface{}) {
msg = "[ERROR] " + msg + "\n"
fmt.Fprintf(os.Stderr, msg, v...)
}
// Info prints an informational message to Stdout.
func Info(msg string, v ...interface{}) {
msg = "[INFO] " + msg + "\n"
fmt.Fprintf(os.Stdout, msg, v...)
}
// Msg prints a raw message to Stdout.
func Msg(msg string, v ...interface{}) {
fmt.Fprintf(os.Stdout, msg, v...)
}
// Success is an achievement marked by pretty output.
func Success(msg string, v ...interface{}) {
msg = "[Success] " + msg + "\n"
fmt.Fprintf(os.Stdout, msg, v...)
}
// Warning emits a warning message.
func Warning(msg string, v ...interface{}) {
msg = "[Warning] " + msg + "\n"
fmt.Fprintf(os.Stdout, msg, v...)

43
glide.lock generated

@ -1,53 +1,56 @@
hash: 4cc1aba06a344d43c0c1005d71dc0659ada5d90f0b2235b1d8e8c7352d1251a7
updated: 2016-01-06T14:30:55.041267875-08:00
hash: fce0581223b80f7a04fbb4ad4bd7ff8fa3d12e879dba894ba448770933731887
updated: 2016-02-02T17:30:13.283644703-07:00
imports:
- name: github.com/codegangsta/cli
version: c31a7975863e7810c92e2e288a9ab074f9a88f29
version: cf1f63a7274872768d4037305d572b70b1199397
- name: github.com/emicklei/go-restful
version: ce94a9f819d7dd2b5599ff0c017b1124595a64fb
version: b86acf97a74ed7603ac78d012f5535b4d587b156
- name: github.com/ghodss/yaml
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
- name: github.com/golang/glog
version: fca8c8854093a154ff1eb580aae10276ad6b1b5f
version: 23def4e6c14b4da8ac2ed8007337bc5eb5007998
- name: github.com/golang/protobuf
version: 2402d76f3d41f928c7902a765dfc872356dd3aad
version: 45bba206dd5270d96bac4942dcfe515726613249
- name: github.com/google/go-github
version: 63fbbb283ce4913a5ac1b6de7abae50dbf594a04
version: b8b4ac742977310ff6e75140a403a38dab109977
subpackages:
- /github
- name: github.com/google/go-querystring
version: 2a60fc2ba6c19de80291203597d752e9ba58e4c0
- name: github.com/gorilla/context
version: 1c83b3eabd45b6d76072b66b746c20815fb2872d
- name: github.com/gorilla/handlers
version: 1af6d56d7cd39d982856bc0cee11142baf392c52
version: b3aff83722cb2ae031a70cae984650e3a16cd20e
- name: github.com/gorilla/mux
version: 26a6070f849969ba72b72256e9f14cf519751690
- name: github.com/gorilla/schema
version: 14c555599c2a4f493c1e13fd1ea6fdf721739028
- name: github.com/kubernetes/deployment-manager
version: 62f19486073edd020a11922304130f0c5c1dff20
version: ""
repo: https://github.com/technosophos/deployment-manager
vcs: git
subpackages:
- /common
- name: github.com/Masterminds/semver
version: c4f7ef0702f269161a60489ccbbc9f1241ad1265
- name: github.com/mjibson/appstats
version: 0542d5f0e87ea3a8fa4174322b9532f5d04f9fa8
- name: golang.org/x/crypto
version: 552e9d568fde9701ea1944fb01c8aadaceaa7353
version: 1f22c0103821b9390939b6776727195525381532
- name: golang.org/x/net
version: 1ade16a5450925b7496e1031938175d1f5d30d31
version: 6c581b96a7d38dd755f986fcf4f29665597694c0
- name: golang.org/x/oauth2
version: 2baa8a1b9338cf13d9eeb27696d761155fa480be
version: 8a57ed94ffd43444c0879fe75701732a38afc985
- name: golang.org/x/text
version: cf4986612c83df6c55578ba198316d1684a9a287
- name: google.golang.com/appengine
version: ""
repo: https://google.golang.com/appengine
version: 5aaa1a807bf8a2f763540b140e7805973476eb88
- name: google.golang.org/api
version: f5b7ec483f357a211c03c6722a840444c2d395dc
version: 8fa1015948e6fc21c025050624e4c4e2f4f405c4
- name: google.golang.org/appengine
version: 54bf9150c922186bfc45a00bf9dfcb91a5063275
version: 6bde959377a90acb53366051d7d587bfd7171354
- name: google.golang.org/cloud
version: 1bff51b8fae8d33cb3dab8f7858c266ce001ee3e
version: 5a3b06f8b5da3b7c3a93da43163b872c86c509ef
- name: google.golang.org/grpc
version: 78905999da08d7f87d5dd11608fa79ff8700daa8
version: 5d64098b94ee9dbbea8ddc130208696bcd199ba4
- name: gopkg.in/yaml.v2
version: f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
devImports: []

@ -1,7 +1,13 @@
package: github.com/deis/helm-dm
ignore:
- google.golang.com/appengine
import:
- package: github.com/codegangsta/cli
- package: github.com/kubernetes/deployment-manager
version: feat/chartfile
repo: https://github.com/technosophos/deployment-manager
vcs: git
subpackages:
- /common
- package: github.com/ghodss/yaml
- package: github.com/Masterminds/semver

@ -13,6 +13,7 @@ func (r RealRunner) Get(stdin []byte, ns string) ([]byte, error) {
return cmd.CombinedOutput()
}
// GetByKind gets a named thing by kind.
func (r RealRunner) GetByKind(kind, name, ns string) (string, error) {
args := []string{"get", kind, name}
@ -37,6 +38,7 @@ func (r PrintRunner) Get(stdin []byte, ns string) ([]byte, error) {
return []byte(cmd.String()), nil
}
// GetByKind gets a named thing by kind.
func (r PrintRunner) GetByKind(kind, name, ns string) (string, error) {
args := []string{"get", kind, name}

Loading…
Cancel
Save