diff --git a/cmd/helm/create.go b/cmd/helm/create.go index e99a05cc9..8f82bc70a 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -5,8 +5,8 @@ import ( "path/filepath" "github.com/spf13/cobra" - - "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/proto/hapi/chart" ) const createDesc = ` @@ -36,8 +36,8 @@ func init() { } var createCmd = &cobra.Command{ - Use: "create [PATH]", - Short: "Create a new chart at the location specified.", + Use: "create NAME", + Short: "Create a new chart with the given name.", Long: createDesc, RunE: runCreate, } @@ -50,12 +50,12 @@ func runCreate(cmd *cobra.Command, args []string) error { cmd.Printf("Creating %s\n", cname) chartname := filepath.Base(cname) - cfile := chart.Chartfile{ + cfile := &chart.Metadata{ Name: chartname, Description: "A Helm chart for Kubernetes", Version: "0.1.0", } - _, err := chart.Create(&cfile, filepath.Dir(cname)) + _, err := chartutil.Create(cfile, filepath.Dir(cname)) return err } diff --git a/pkg/chartutil/chartfile.go b/pkg/chartutil/chartfile.go index 70bfc7ac8..84d1d670c 100644 --- a/pkg/chartutil/chartfile.go +++ b/pkg/chartutil/chartfile.go @@ -26,3 +26,14 @@ func LoadChartfile(filename string) (*chart.Metadata, error) { } return UnmarshalChartfile(b) } + +// SaveChartfile saves the given metadata as a Chart.yaml file at the given path. +// +// 'filename' should be the complete path and filename ('foo/Chart.yaml') +func SaveChartfile(filename string, cf *chart.Metadata) error { + out, err := yaml.Marshal(cf) + if err != nil { + return err + } + return ioutil.WriteFile(filename, out, 0755) +} diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go new file mode 100644 index 000000000..fa6634852 --- /dev/null +++ b/pkg/chartutil/create.go @@ -0,0 +1,78 @@ +package chartutil + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/kubernetes/helm/pkg/proto/hapi/chart" +) + +const ( + // ChartfileName is the default Chart file name. + ChartfileName = "Chart.yaml" + // ValuesfileName is the default values file name. + ValuesfileName = "values.yaml" + // TemplatesDir is the relative directory name for templates. + TemplatesDir = "templates" + // ChartsDir is the relative directory name for charts dependencies. + ChartsDir = "charts" +) + +const defaultValues = `# Default values for %s. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name: value +` + +// Create creates a new chart in a directory. +// +// Inside of dir, this will create a directory based on the name of +// chartfile.Name. It will then write the Chart.yaml into this directory and +// create the (empty) appropriate directories. +// +// The returned string will point to the newly created directory. It will be +// an absolute path, even if the provided base directory was relative. +// +// If dir does not exist, this will return an error. +// If Chart.yaml or any directories cannot be created, this will return an +// error. In such a case, this will attempt to clean up by removing the +// new chart directory. +func Create(chartfile *chart.Metadata, dir string) (string, error) { + path, err := filepath.Abs(dir) + if err != nil { + return path, err + } + + if fi, err := os.Stat(path); err != nil { + return path, err + } else if !fi.IsDir() { + return path, fmt.Errorf("no such directory %s", path) + } + + n := chartfile.Name + cdir := filepath.Join(path, n) + if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() { + return cdir, fmt.Errorf("file %s already exists and is not a directory", cdir) + } + if err := os.MkdirAll(cdir, 0755); err != nil { + return cdir, err + } + + if err := SaveChartfile(filepath.Join(cdir, ChartfileName), chartfile); err != nil { + return cdir, err + } + + val := []byte(fmt.Sprintf(defaultValues, chartfile.Name)) + if err := ioutil.WriteFile(filepath.Join(cdir, ValuesfileName), val, 0644); err != nil { + return cdir, err + } + + for _, d := range []string{TemplatesDir, ChartsDir} { + if err := os.MkdirAll(filepath.Join(cdir, d), 0755); err != nil { + return cdir, err + } + } + return cdir, nil +} diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go new file mode 100644 index 000000000..19ee11ed6 --- /dev/null +++ b/pkg/chartutil/create_test.go @@ -0,0 +1,53 @@ +package chartutil + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/kubernetes/helm/pkg/proto/hapi/chart" +) + +func TestCreate(t *testing.T) { + tdir, err := ioutil.TempDir("", "helm-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tdir) + + cf := &chart.Metadata{Name: "foo"} + + c, err := Create(cf, tdir) + if err != nil { + t.Fatal(err) + } + + dir := filepath.Join(tdir, "foo") + + mychart, err := LoadDir(c) + if err != nil { + t.Fatalf("Failed to load newly created chart %q: %s", c, err) + } + + if mychart.Metadata.Name != "foo" { + t.Errorf("Expected name to be 'foo', got %q", mychart.Metadata.Name) + } + + for _, d := range []string{TemplatesDir, ChartsDir} { + 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, ValuesfileName} { + 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) + } + } + +}