diff --git a/cmd/helm/repository.go b/cmd/helm/repository.go index 9ff75eb07..2084e753d 100644 --- a/cmd/helm/repository.go +++ b/cmd/helm/repository.go @@ -43,6 +43,14 @@ const repoDesc = `Helm repositories store Helm charts. For more details, use 'helm repo CMD -h'. ` +const addRepoDesc = `The add repository command is used to add a name a repository url to your + chart repository list. The repository url must begin with a valid protocoal + These include https, http, and gs. + + A valid command might look like: + $ helm repo add charts gs://kubernetes-charts +` + func repoCommands() cli.Command { return cli.Command{ Name: "repository", @@ -51,10 +59,11 @@ func repoCommands() cli.Command { Description: repoDesc, Subcommands: []cli.Command{ { - Name: "add", - Usage: "Add a chart repository to the remote manager.", - ArgsUsage: "[NAME] [REPOSITORY_URL]", - Action: func(c *cli.Context) { run(c, addRepo) }, + Name: "add", + Usage: "Add a chart repository to the remote manager.", + Description: addRepoDesc, + ArgsUsage: "[NAME] [REPOSITORY_URL]", + Action: func(c *cli.Context) { run(c, addRepo) }, }, { Name: "list", @@ -66,7 +75,7 @@ func repoCommands() cli.Command { Name: "remove", Aliases: []string{"rm"}, Usage: "Remove a chart repository from the remote manager.", - ArgsUsage: "REPOSITORY_URL", + ArgsUsage: "REPOSITORY_NAME", Action: func(c *cli.Context) { run(c, removeRepo) }, }, }, @@ -83,7 +92,6 @@ func addRepo(c *cli.Context) error { payload, _ := json.Marshal(repo.Repo{URL: repoURL, Name: name}) msg := "" if _, err := NewClient(c).Post(chartRepoPath, payload, &msg); err != nil { - //TODO: Return more specific errors to the user return err } format.Info(name + " has been added to your chart repositories!") diff --git a/cmd/manager/chartrepos.go b/cmd/manager/chartrepos.go index 64ae8ddd8..2c056e4dd 100644 --- a/cmd/manager/chartrepos.go +++ b/cmd/manager/chartrepos.go @@ -57,6 +57,9 @@ func addChartRepoHandlerFunc(w http.ResponseWriter, r *http.Request, c *router.C httputil.BadRequest(w, r, err) return nil } + if err := repo.ValidateRepoURL(cr.URL); err != nil { + return err + } if err := c.Manager.AddRepo(cr); err != nil { httputil.BadRequest(w, r, err) diff --git a/pkg/repo/repo.go b/pkg/repo/repo.go index ce2a29cb3..ce3e9c3c9 100644 --- a/pkg/repo/repo.go +++ b/pkg/repo/repo.go @@ -17,10 +17,16 @@ limitations under the License. package repo import ( + "errors" "fmt" "net/url" + "regexp" + "strings" ) +// URL is the url pattern used to check if a given repo url is valid +const URL string = `^((http|gs|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$` + // NewRepo takes params and returns a IRepo func NewRepo(URL, credentialName, repoName, repoFormat, repoType string) (IRepo, error) { return newRepo(URL, credentialName, repoName, ERepoFormat(repoFormat), ERepoType(repoType)) @@ -113,3 +119,26 @@ func validateRepo(tr IRepo, wantURL, wantCredentialName string, wantFormat ERepo return nil } + +// ValidateRepoURL checks if the string is a valid URL. +// This was inspired by the IsURL function in govalidator https://github.com/asaskevich/govalidator +func ValidateRepoURL(str string) error { + err := errors.New("Invalid Repository URL") + if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") { + return err + } + u, err := url.Parse(str) + if err != nil { + return err + } + if strings.HasPrefix(u.Host, ".") { + return err + } + if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { + return err + } + if !regexp.MustCompile(URL).MatchString(str) { + return err + } + return nil +} diff --git a/pkg/repo/repo_test.go b/pkg/repo/repo_test.go index 1415f7332..9bce229e3 100644 --- a/pkg/repo/repo_test.go +++ b/pkg/repo/repo_test.go @@ -65,3 +65,26 @@ func TestInvalidRepoFormat(t *testing.T) { t.Fatalf("expected error did not occur for invalid format") } } + +func TestValidateRepoURL(t *testing.T) { + validURLs := []string{ + "https://host/bucket", + "http://host/bucket", + "gs://bucket-name", + } + invalidURLs := []string{"charts"} + + for _, url := range validURLs { + err := ValidateRepoURL(url) + if err != nil { + t.Fatalf("Expected repo url: %v to be valid but threw an error") + } + } + + for _, url := range invalidURLs { + err := ValidateRepoURL(url) + if err == nil { + t.Fatalf("Expected repo url: %v to be invalid but did not throw an error") + } + } +}