Merge pull request #20 from technosophos/feat/helm-create

feat(helm): implement 'helm create'
pull/613/head
Matt Butcher 9 years ago
commit b9dbc0577b

@ -0,0 +1,63 @@
package main
import (
"errors"
"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 # Information about your chart
|
|- values.toml # The default values for your templates
|
|- charts/ # Charts that this chart depends on
|
|- templates/ # The template files
'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",
}
if _, err := chart.Create(&cfile, filepath.Dir(cname)); err != nil {
return err
}
return nil
}

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

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

@ -34,11 +34,16 @@ const ChartfileName string = "Chart.yaml"
const (
preTemplates string = "templates/"
preHooks string = "hooks/"
preDocs string = "docs/"
preIcon string = "icon.svg"
preValues string = "values.toml"
preCharts string = "charts/"
)
const defaultValues = `# Default values for %s.
# This is a TOML-formatted file. https://github.com/toml-lang/toml
# Declare name/value pairs to be passed into your templates.
# name = "value"
`
var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")
// Chart represents a complete chart.
@ -78,28 +83,14 @@ func (c *Chart) Dir() string {
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.
func (c *Chart) TemplatesDir() string {
return filepath.Join(c.loader.dir(), preTemplates)
}
// Icon returns the path to the icon.svg file.
//
// If an icon is not found in the chart, this will return an error.
func (c *Chart) Icon() (string, error) {
i := filepath.Join(c.Dir(), preIcon)
_, err := os.Stat(i)
return i, err
// ChartsDir returns teh directory where dependency charts are stored.
func (c *Chart) ChartsDir() string {
return filepath.Join(c.loader.dir(), preCharts)
}
// chartLoader provides load, close, and save implementations for a chart.
@ -174,26 +165,24 @@ func Create(chartfile *Chartfile, dir string) (*Chart, error) {
n := fname(chartfile.Name)
cdir := filepath.Join(path, n)
if _, err := os.Stat(cdir); err == nil {
return nil, fmt.Errorf("directory already exists: %s", cdir)
if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() {
return nil, fmt.Errorf("file %s already exists and is not a directory", cdir)
}
if err := os.MkdirAll(cdir, 0755); err != nil {
return nil, err
}
rollback := func() {
// TODO: Should we log failures here?
os.RemoveAll(cdir)
if err := chartfile.Save(filepath.Join(cdir, ChartfileName)); err != nil {
return nil, err
}
if err := chartfile.Save(filepath.Join(cdir, ChartfileName)); err != nil {
rollback()
val := []byte(fmt.Sprintf(defaultValues, chartfile.Name))
if err := ioutil.WriteFile(filepath.Join(cdir, preValues), val, 0644); err != nil {
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 {
rollback()
return nil, err
}
}

@ -19,6 +19,7 @@ package chart
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"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) {
c, err := Load(testarchive)
if err != nil {
@ -102,9 +141,9 @@ func TestChart(t *testing.T) {
}
dir := c.Dir()
d := c.DocsDir()
if d != filepath.Join(dir, preDocs) {
t.Errorf("Unexpectedly, docs are in %s", d)
d := c.ChartsDir()
if d != filepath.Join(dir, preCharts) {
t.Errorf("Unexpectedly, charts are in %s", d)
}
d = c.TemplatesDir()

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

Loading…
Cancel
Save