diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index def0dd1da..43a032c00 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -7,12 +7,38 @@ import ( ) var stdout = os.Stdout +var helmHome string + +var globalUsage = `The Kubernetes package manager + +To begin working with Helm, run the 'helm init' command: + +$ helm init + +This will install Tiller to your running Kubernetes cluster. +It will also set up any necessary local configuration. + +Commond actions from this point on include: + +- helm search: search for charts +- helm fetch: download a chart to your local directory to view +- helm install: upload the chart to Kubernetes +- helm list: list releases of charts + +ENVIRONMENT: +$HELM_HOME: Set an alternative location for Helm files. + By default, these are stored in ~/.helm +` // RootCommand is the top-level command for Helm. var RootCommand = &cobra.Command{ Use: "helm", Short: "The Helm package manager for Kubernetes.", - Long: `Do long help here.`, + Long: globalUsage, +} + +func init() { + RootCommand.PersistentFlags().StringVar(&helmHome, "home", "$HOME/.helm", "location of you Helm files [$HELM_HOME]") } func main() { diff --git a/cmd/helm/home.go b/cmd/helm/home.go new file mode 100644 index 000000000..3b091931b --- /dev/null +++ b/cmd/helm/home.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +var longHomeHelp = ` +This command displays the location of HELM_HOME. This is where +any helm configuration files live. +` + +var homeCommand = &cobra.Command{ + Use: "home", + Short: "Displays the location of HELM_HOME", + Long: longHomeHelp, + Run: Home, +} + +func init() { + RootCommand.AddCommand(homeCommand) +} + +func Home(cmd *cobra.Command, args []string) { + fmt.Println("helm home was called") +} diff --git a/cmd/helm/init.go b/cmd/helm/init.go index 7a7f53b31..5155c6724 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -3,6 +3,9 @@ package main import ( "errors" "fmt" + "io/ioutil" + "os" + "path/filepath" "github.com/deis/tiller/pkg/client" "github.com/deis/tiller/pkg/kubectl" @@ -14,6 +17,10 @@ This command installs Tiller (the helm server side component) onto your Kubernetes Cluster and sets up local configuration in $HELM_HOME (default: ~/.helm/) ` +const repositoriesPath = ".repositories" +const cachePath = "cache" + +var defaultRepo = map[string]string{"default-name": "default-url"} var tillerImg string func init() { @@ -34,18 +41,30 @@ func RunInit(cmd *cobra.Command, args []string) error { return errors.New("This command does not accept arguments. \n") } + if err := EnsureHome(os.ExpandEnv(helmHome)); err != nil { + return err + } + + if err := installTiller(); err != nil { + return err + } + + fmt.Printf("Tiller (the helm server side component) has been installed into your Kubernetes Cluster.\n$HELM_HOME has also been configured at %s.\nHappy Helming!\n", helmHome) + return nil +} + +func installTiller() error { // TODO: take value of global flag kubectl and pass that in runner := buildKubectlRunner("") i := client.NewInstaller() i.Tiller["Image"] = tillerImg - out, err := i.Install(runner) + if err != nil { return fmt.Errorf("error installing %s %s", string(out), err) } - fmt.Printf("Tiller (the helm server side component) has been installed into your Kubernetes Cluster.\n") return nil } @@ -55,3 +74,40 @@ func buildKubectlRunner(kubectlPath string) kubectl.Runner { } return &kubectl.RealRunner{} } + +// EnsureHome checks to see if $HELM_HOME exists +// +// If $HELM_HOME does not exist, this function will create it. +func EnsureHome(home string) error { + configDirectories := []string{home, CacheDirectory(home)} + + for _, p := range configDirectories { + if fi, err := os.Stat(p); err != nil { + fmt.Printf("Creating %s \n", p) + if err := os.MkdirAll(p, 0755); err != nil { + return fmt.Errorf("Could not create %s: %s", p, err) + } + } else if !fi.IsDir() { + return fmt.Errorf("%s must be a directory.", p) + } + } + + repoPath := RepositoriesFile(home) + if fi, err := os.Stat(repoPath); err != nil { + fmt.Printf("Creating %s \n", repoPath) + if err := ioutil.WriteFile(repoPath, []byte("test-charts: https://www.googleapis.com/storage/v1/b/test-charts/o\n"), 0644); err != nil { + return err + } + } else if fi.IsDir() { + return fmt.Errorf("%s must be a file, not a directory.", repoPath) + } + return nil +} + +func CacheDirectory(home string) string { + return filepath.Join(home, cachePath) +} + +func RepositoriesFile(home string) string { + return filepath.Join(home, repositoriesPath) +} diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go index 59a45c6ab..31d75bc8e 100644 --- a/cmd/helm/init_test.go +++ b/cmd/helm/init_test.go @@ -1,10 +1,36 @@ package main import ( + "io/ioutil" + "os" "testing" ) -func TestInit(t *testing.T) { - //TODO: call command and make sure no error is returned - //TODO: check local config +func TestEnsureHome(t *testing.T) { + home := CreateTmpHome() + if err := EnsureHome(home); err != nil { + t.Errorf("%s", err) + } + + dirs := []string{home, CacheDirectory(home)} + for _, dir := range dirs { + if fi, err := os.Stat(dir); err != nil { + t.Errorf("%s", err) + } else if !fi.IsDir() { + t.Errorf("%s is not a directory", fi) + } + } + + if fi, err := os.Stat(RepositoriesFile(home)); err != nil { + t.Errorf("%s", err) + } else if fi.IsDir() { + t.Errorf("%s should not be a directory", fi) + } + +} + +func CreateTmpHome() string { + tmpHome, _ := ioutil.TempDir("", "helm_home") + defer os.Remove(tmpHome) + return tmpHome }