feat(helm): implement 'helm create'

pull/613/head
Matt Butcher 9 years ago
parent 8858056386
commit a97bf8b7c0

@ -0,0 +1,77 @@
package main
import (
"errors"
"os/user"
"path/filepath"
"github.com/deis/tiller/pkg/chart"
"github.com/spf13/cobra"
)
const createDesc = `
This command creates a chart directory along with the common files and
directories used in a chart.
For example, 'helm create foo' will create a directory structure that looks
something like this:
foo/
|- Chart.yaml
|
|- values.toml
|
|- templates/
'helm create' takes a path for an argument. If directories in the given path
do not exist, Helm will attempt to create them as it goes. If the given
destination exists and there are files in that directory, conflicting files
will be overwritten, but other files will be left alone.
`
func init() {
RootCommand.AddCommand(createCmd)
}
var createCmd = &cobra.Command{
Use: "create [PATH]",
Short: "Create a new chart at the location specified.",
Long: createDesc,
RunE: runCreate,
}
func runCreate(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("the name of the new chart is required")
}
cname := args[0]
cmd.Printf("Creating %s\n", cname)
chartname := filepath.Base(cname)
cfile := chart.Chartfile{
Name: chartname,
Description: "A Helm chart for Kubernetes",
Version: "0.1.0",
Maintainers: []*chart.Maintainer{
{Name: username()},
},
}
if _, err := chart.Create(&cfile, filepath.Dir(cname)); err != nil {
return err
}
return nil
}
func username() string {
uname := "Unknown"
u, err := user.Current()
if err == nil {
uname = u.Name
if uname == "" {
uname = u.Username
}
}
return uname
}

@ -8,6 +8,7 @@ import (
var stdout = os.Stdout var stdout = os.Stdout
// RootCommand is the top-level command for Helm.
var RootCommand = &cobra.Command{ var RootCommand = &cobra.Command{
Use: "helm", Use: "helm",
Short: "The Helm package manager for Kubernetes.", Short: "The Helm package manager for Kubernetes.",

@ -9,7 +9,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
const longDesc = ` const installDesc = `
This command installs Tiller (the helm server side component) onto your This command installs Tiller (the helm server side component) onto your
Kubernetes Cluster and sets up local configuration in $HELM_HOME (default: ~/.helm/) Kubernetes Cluster and sets up local configuration in $HELM_HOME (default: ~/.helm/)
` `
@ -24,7 +24,7 @@ func init() {
var initCmd = &cobra.Command{ var initCmd = &cobra.Command{
Use: "init", Use: "init",
Short: "Initialize Helm on both client and server.", Short: "Initialize Helm on both client and server.",
Long: longDesc, Long: installDesc,
RunE: RunInit, RunE: RunInit,
} }

@ -34,9 +34,8 @@ const ChartfileName string = "Chart.yaml"
const ( const (
preTemplates string = "templates/" preTemplates string = "templates/"
preHooks string = "hooks/" preValues string = "values.toml"
preDocs string = "docs/" preCharts string = "charts/"
preIcon string = "icon.svg"
) )
var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")
@ -78,28 +77,14 @@ func (c *Chart) Dir() string {
return c.loader.dir() return c.loader.dir()
} }
// DocsDir returns the directory where the chart's documentation is stored.
func (c *Chart) DocsDir() string {
return filepath.Join(c.loader.dir(), preDocs)
}
// HooksDir returns the directory where the hooks are stored.
func (c *Chart) HooksDir() string {
return filepath.Join(c.loader.dir(), preHooks)
}
// TemplatesDir returns the directory where the templates are stored. // TemplatesDir returns the directory where the templates are stored.
func (c *Chart) TemplatesDir() string { func (c *Chart) TemplatesDir() string {
return filepath.Join(c.loader.dir(), preTemplates) return filepath.Join(c.loader.dir(), preTemplates)
} }
// Icon returns the path to the icon.svg file. // ChartsDir returns teh directory where dependency charts are stored.
// func (c *Chart) ChartsDir() string {
// If an icon is not found in the chart, this will return an error. return filepath.Join(c.loader.dir(), preCharts)
func (c *Chart) Icon() (string, error) {
i := filepath.Join(c.Dir(), preIcon)
_, err := os.Stat(i)
return i, err
} }
// chartLoader provides load, close, and save implementations for a chart. // chartLoader provides load, close, and save implementations for a chart.
@ -174,26 +159,24 @@ func Create(chartfile *Chartfile, dir string) (*Chart, error) {
n := fname(chartfile.Name) n := fname(chartfile.Name)
cdir := filepath.Join(path, n) cdir := filepath.Join(path, n)
if _, err := os.Stat(cdir); err == nil { if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() {
return nil, fmt.Errorf("directory already exists: %s", cdir) return nil, fmt.Errorf("file %s already exists and is not a directory", cdir)
} }
if err := os.MkdirAll(cdir, 0755); err != nil { if err := os.MkdirAll(cdir, 0755); err != nil {
return nil, err return nil, err
} }
rollback := func() { if err := chartfile.Save(filepath.Join(cdir, ChartfileName)); err != nil {
// TODO: Should we log failures here? return nil, err
os.RemoveAll(cdir)
} }
if err := chartfile.Save(filepath.Join(cdir, ChartfileName)); err != nil { val := []byte(fmt.Sprintf("# Default Values for %s\n", chartfile.Name))
rollback() if err := ioutil.WriteFile(filepath.Join(cdir, preValues), val, 0644); err != nil {
return nil, err return nil, err
} }
for _, d := range []string{preHooks, preDocs, preTemplates} { for _, d := range []string{preTemplates, preCharts} {
if err := os.MkdirAll(filepath.Join(cdir, d), 0755); err != nil { if err := os.MkdirAll(filepath.Join(cdir, d), 0755); err != nil {
rollback()
return nil, err return nil, err
} }
} }

@ -19,6 +19,7 @@ package chart
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"testing" "testing"
@ -47,6 +48,44 @@ func TestLoadDir(t *testing.T) {
} }
} }
func TestCreate(t *testing.T) {
tdir, err := ioutil.TempDir("", "helm-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tdir)
cf := &Chartfile{Name: "foo"}
c, err := Create(cf, tdir)
if err != nil {
t.Fatal(err)
}
dir := filepath.Join(tdir, "foo")
if c.Chartfile().Name != "foo" {
t.Errorf("Expected name to be 'foo', got %q", c.Chartfile().Name)
}
for _, d := range []string{preTemplates, preCharts} {
if fi, err := os.Stat(filepath.Join(dir, d)); err != nil {
t.Errorf("Expected %s dir: %s", d, err)
} else if !fi.IsDir() {
t.Errorf("Expected %s to be a directory.", d)
}
}
for _, f := range []string{ChartfileName, preValues} {
if fi, err := os.Stat(filepath.Join(dir, f)); err != nil {
t.Errorf("Expected %s file: %s", f, err)
} else if fi.IsDir() {
t.Errorf("Expected %s to be a fle.", f)
}
}
}
func TestLoad(t *testing.T) { func TestLoad(t *testing.T) {
c, err := Load(testarchive) c, err := Load(testarchive)
if err != nil { if err != nil {
@ -102,9 +141,9 @@ func TestChart(t *testing.T) {
} }
dir := c.Dir() dir := c.Dir()
d := c.DocsDir() d := c.ChartsDir()
if d != filepath.Join(dir, preDocs) { if d != filepath.Join(dir, preCharts) {
t.Errorf("Unexpectedly, docs are in %s", d) t.Errorf("Unexpectedly, charts are in %s", d)
} }
d = c.TemplatesDir() d = c.TemplatesDir()

@ -20,7 +20,7 @@ type Installer struct {
Tiller map[string]interface{} Tiller map[string]interface{}
} }
// New Installer creates a new Installer // NewInstaller creates a new Installer
func NewInstaller() *Installer { func NewInstaller() *Installer {
return &Installer{ return &Installer{
Metadata: map[string]interface{}{}, Metadata: map[string]interface{}{},

Loading…
Cancel
Save