diff --git a/cmd/helm/repository.go b/cmd/helm/repository.go index 9ff75eb07..6b6ca38ea 100644 --- a/cmd/helm/repository.go +++ b/cmd/helm/repository.go @@ -19,7 +19,10 @@ package main import ( "encoding/json" "errors" + "net/url" "path/filepath" + "regexp" + "strings" "github.com/codegangsta/cli" "github.com/kubernetes/helm/pkg/format" @@ -32,6 +35,9 @@ func init() { const chartRepoPath = "repositories" +// 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]*)?$` + const repoDesc = `Helm repositories store Helm charts. The repository commands are used to manage which Helm repositories Helm may @@ -43,6 +49,16 @@ const repoDesc = `Helm repositories store Helm charts. For more details, use 'helm repo CMD -h'. ` +const addRepoDesc = `helm repository|repo add [NAME] [REPOSITORY_URL] + + 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 +67,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", @@ -80,10 +97,13 @@ func addRepo(c *cli.Context) error { } name := args[0] repoURL := args[1] + valid := IsValidURL(repoURL) + if !valid { + return errors.New(repoURL + " is not a valid URL") + } 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!") @@ -120,3 +140,22 @@ func removeRepo(c *cli.Context) error { format.Msg(name + " has been removed.\n") return nil } + +// IsValidURL checks if the string is a valid URL. +// This was inspired by the IsURL function in govalidator https://github.com/asaskevich/govalidator +func IsValidURL(str string) bool { + if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") { + return false + } + u, err := url.Parse(str) + if err != nil { + return false + } + if strings.HasPrefix(u.Host, ".") { + return false + } + if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { + return false + } + return regexp.MustCompile(URL).MatchString(str) +} diff --git a/cmd/helm/repository_test.go b/cmd/helm/repository_test.go new file mode 100644 index 000000000..d3327599b --- /dev/null +++ b/cmd/helm/repository_test.go @@ -0,0 +1,37 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "testing" +) + +func TestHasValidPrefix(t *testing.T) { + tests := map[string]bool{ + "https://host/bucket": true, + "http://host/bucket": true, + "gs://bucket-name": true, + "charts": false, + } + + for url, expectedResult := range tests { + result := IsValidURL(url) + if result != expectedResult { + t.Errorf("Expected: %v, Got: %v for url: %v", expectedResult, result, url) + } + } +}