diff --git a/cmd/helm/fetch.go b/cmd/helm/fetch.go index c14c4f103..ca962ffb8 100644 --- a/cmd/helm/fetch.go +++ b/cmd/helm/fetch.go @@ -4,8 +4,12 @@ import ( "fmt" "io" "net/http" + "net/url" "os" + "strings" + "github.com/kubernetes/helm/pkg/chart" + "github.com/kubernetes/helm/pkg/repo" "github.com/spf13/cobra" ) @@ -14,33 +18,92 @@ func init() { } var fetchCmd = &cobra.Command{ - Use: "fetch", - Short: "Download a chart from a repository and unpack it in local directory.", + Use: "fetch [chart URL | repo/chartname]", + Short: "Download a chart from a repository and (optionally) unpack it in local directory.", Long: "", RunE: fetch, } func fetch(cmd *cobra.Command, args []string) error { - // parse args + if len(args) == 0 { + return fmt.Errorf("This command needs at least one argument, url or repo/name of the chart.") + } + + f, err := repo.LoadRepositoriesFile(repositoriesFile()) + if err != nil { + return err + } + // get download url - // call download url - out, err := os.Create("nginx-2.0.0.tgz") + u, err := mapRepoArg(args[0], f.Repositories) + if err != nil { + return err + } + + resp, err := http.Get(u.String()) + if err != nil { + return err + } + if resp.StatusCode != 200 { + return fmt.Errorf("Failed to fetch %s : %s", u.String(), resp.Status) + } + + defer resp.Body.Close() + // TODO(vaikas): Implement untar / flag + untar := false + if untar { + return untarChart(resp.Body) + } + p := strings.Split(u.String(), "/") + return saveChartFile(p[len(p)-1], resp.Body) +} + +// mapRepoArg figures out which format the argument is given, and creates a fetchable +// url from it. +func mapRepoArg(arg string, r map[string]string) (*url.URL, error) { + // See if it's already a full URL. + u, err := url.ParseRequestURI(arg) + if err == nil { + // If it has a scheme and host and path, it's a full URL + if u.IsAbs() && len(u.Host) > 0 && len(u.Path) > 0 { + return u, nil + } + return nil, fmt.Errorf("Invalid chart url format: %s", arg) + } + // See if it's of the form: repo/path_to_chart + p := strings.Split(arg, "/") + if len(p) > 1 { + if baseURL, ok := r[p[0]]; ok { + if !strings.HasSuffix(baseURL, "/") { + baseURL = baseURL + "/" + } + return url.ParseRequestURI(baseURL + strings.Join(p[1:], "/")) + } + return nil, fmt.Errorf("No such repo: %s", p[0]) + } + return nil, fmt.Errorf("Invalid chart url format: %s", arg) +} + +func untarChart(r io.Reader) error { + c, err := chart.LoadDataFromReader(r) + if err != nil { + return err + } + if c == nil { + return fmt.Errorf("Failed to untar the chart") + } + return fmt.Errorf("Not implemented yee") + +} + +func saveChartFile(c string, r io.Reader) error { + // Grab the chart name that we'll use for the name of the file to download to. + out, err := os.Create(c) if err != nil { return err } defer out.Close() - resp, err := http.Get("http://localhost:8879/charts/nginx-2.0.0.tgz") - fmt.Println("after req") - // unpack file - if err != nil { - return err - } - defer resp.Body.Close() - - _, err = io.Copy(out, resp.Body) - if err != nil { - return err - } - return nil + _, err = io.Copy(out, r) + return err } diff --git a/cmd/helm/fetch_test.go b/cmd/helm/fetch_test.go new file mode 100644 index 000000000..8c17ef71c --- /dev/null +++ b/cmd/helm/fetch_test.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + // "io" + // "net/http" + //"net/url" + // "os" + + "testing" +) + +type testCase struct { + in string + expectedErr error + expectedOut string +} + +var repos = map[string]string{ + "local": "http://localhost:8879/charts", + "someother": "http://storage.googleapis.com/mycharts", +} + +var testCases = []testCase{ + {"bad", fmt.Errorf("Invalid chart url format: bad"), ""}, + {"http://", fmt.Errorf("Invalid chart url format: http://"), ""}, + {"http://example.com", fmt.Errorf("Invalid chart url format: http://example.com"), ""}, + {"http://example.com/foo/bar", nil, "http://example.com/foo/bar"}, + {"local/nginx-2.0.0.tgz", nil, "http://localhost:8879/charts/nginx-2.0.0.tgz"}, + {"nonexistentrepo/nginx-2.0.0.tgz", fmt.Errorf("No such repo: nonexistentrepo"), ""}, +} + +func testRunner(t *testing.T, tc testCase) { + u, err := mapRepoArg(tc.in, repos) + if (tc.expectedErr == nil && err != nil) || + (tc.expectedErr != nil && err == nil) || + (tc.expectedErr != nil && err != nil && tc.expectedErr.Error() != err.Error()) { + t.Errorf("Expected mapRepoArg to fail with input %s %v but got %v", tc.in, tc.expectedErr, err) + } + + if (u == nil && len(tc.expectedOut) != 0) || + (u != nil && len(tc.expectedOut) == 0) || + (u != nil && tc.expectedOut != u.String()) { + t.Errorf("Expected %s to map to fetch url %v but got %v", tc.in, tc.expectedOut, u) + } + +} + +func TestMappings(t *testing.T) { + for _, tc := range testCases { + testRunner(t, tc) + } +} diff --git a/cmd/helm/init.go b/cmd/helm/init.go index 5ea4e4de2..5ae526871 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -84,7 +84,7 @@ func ensureHome() error { if _, err := os.Create(repoFile); err != nil { return err } - if err := insertRepoLine("local", "localhost:8879/charts"); err != nil { + if err := insertRepoLine("local", "http://localhost:8879/charts"); err != nil { return err } } else if fi.IsDir() {