Commit 9d8567e0 authored by jackgr's avatar jackgr

Define basic repository

parent 453b6719
...@@ -17,83 +17,116 @@ limitations under the License. ...@@ -17,83 +17,116 @@ limitations under the License.
package repo package repo
import ( import (
"github.com/kubernetes/helm/pkg/chart"
"github.com/kubernetes/helm/pkg/common"
"fmt" "fmt"
"net/url" "net/url"
"regexp"
) )
// ChartRepo abstracts a place that holds charts, which can be // repo describes a repository
// used in a Deployment Manager configuration. There can be multiple type repo struct {
// ChartRepo implementations. Name string `json:"name"` // Friendly name for this repository
type ChartRepo interface { URL string `json:"url"` // URL to the root of this repository
// GetRepoName returns the name of this ChartRepo. CredentialName string `json:"credentialname"` // Credential name used to access this repository
GetRepoName() string Format RepoFormat `json:"format"` // Format of this repository
// GetRepoType returns the type of this repo. Type RepoType `json:"type"` // Technology implementing this repository
GetRepoType() common.RepoType
// GetRepoURL returns the URL to the root of this ChartRepo.
GetRepoURL() string
// GetRepoFormat returns the format of this ChartRepo.
GetRepoFormat() common.RepoFormat
// ListCharts lists charts in this repository whose string values
// conform to the supplied regular expression or all charts if regex is nil
ListCharts(regex *regexp.Regexp) ([]string, error)
// GetChart retrieves, unpacks and returns a chart by name.
GetChart(name string) (*chart.Chart, error)
}
// ObjectStorageRepo abstracts a repository that resides in an Object Storage, for
// example Google Cloud Storage or AWS S3, etc.
type ObjectStorageRepo interface {
ChartRepo // An ObjectStorageRepo is a ChartRepo
GetBucket() string
} }
type chartRepo struct { func NewRepo(name, URL, credentialName, repoFormat, repoType string) (Repo, error) {
Name string `json:"name,omitempty"` // The name of this ChartRepo return newRepo(name, URL, credentialName, RepoFormat(repoFormat), RepoType(repoType))
URL string `json:"url,omitempty"` // The URL to the root of this ChartRepo
Format common.RepoFormat `json:"format,omitempty"` // The format of this ChartRepo
Type common.RepoType `json:"type,omitempty"` // The type of this ChartRepo
} }
// ChartNameMatcher matches the chart name format func newRepo(name, URL, credentialName string, repoFormat RepoFormat, repoType RepoType) (*repo, error) {
var ChartNameMatcher = regexp.MustCompile("(.*)-(.*).tgz") if name == "" {
return nil, fmt.Errorf("name must not be empty")
}
func newRepo(name, URL, format, t string) (*chartRepo, error) {
_, err := url.Parse(URL) _, err := url.Parse(URL)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid URL (%s): %s", URL, err) return nil, fmt.Errorf("invalid URL (%s): %s", URL, err)
} }
result := &chartRepo{ if credentialName == "" {
credentialName = "default"
}
if err := validateRepoFormat(repoFormat); err != nil {
return nil, err
}
r := &repo{
Name: name, Name: name,
Type: repoType,
URL: URL, URL: URL,
Format: common.RepoFormat(format), Format: repoFormat,
Type: common.RepoType(t), CredentialName: credentialName,
} }
return result, nil return r, nil
}
// Currently, only flat repositories are supported.
func validateRepoFormat(repoFormat RepoFormat) error {
switch repoFormat {
case FlatRepoFormat:
return nil
}
return fmt.Errorf("unknown repository format: %s", repoFormat)
}
// GetName returns the friendly name of this repository.
func (r *repo) GetName() string {
return r.Name
} }
// GetRepoName returns the name of this ChartRepo. // GetType returns the technology implementing this repository.
func (cr *chartRepo) GetRepoName() string { func (r *repo) GetType() RepoType {
return cr.Name return r.Type
} }
// GetRepoType returns the type of this repo. // GetURL returns the URL to the root of this repository.
func (cr *chartRepo) GetRepoType() common.RepoType { func (r *repo) GetURL() string {
return cr.Type return r.URL
} }
// GetRepoURL returns the URL to the root of this ChartRepo. // GetFormat returns the format of this repository.
func (cr *chartRepo) GetRepoURL() string { func (r *repo) GetFormat() RepoFormat {
return cr.URL return r.Format
} }
// GetRepoFormat returns the format of this ChartRepo. // GetCredentialName returns the credential name used to access this repository.
func (cr *chartRepo) GetRepoFormat() common.RepoFormat { func (r *repo) GetCredentialName() string {
return cr.Format return r.CredentialName
}
func validateRepo(tr Repo, wantName, wantURL, wantCredentialName string, wantFormat RepoFormat, wantType RepoType) error {
haveName := tr.GetName()
if haveName != wantName {
return fmt.Errorf("unexpected repo name; want: %s, have %s.", wantName, haveName)
}
haveURL := tr.GetURL()
if haveURL != wantURL {
return fmt.Errorf("unexpected repo url; want: %s, have %s.", wantURL, haveURL)
}
haveCredentialName := tr.GetCredentialName()
if wantCredentialName == "" {
wantCredentialName = "default"
}
if haveCredentialName != wantCredentialName {
return fmt.Errorf("unexpected repo credential name; want: %s, have %s.", wantCredentialName, haveCredentialName)
}
haveFormat := tr.GetFormat()
if haveFormat != wantFormat {
return fmt.Errorf("unexpected repo format; want: %s, have %s.", wantFormat, haveFormat)
}
haveType := tr.GetType()
if haveType != wantType {
return fmt.Errorf("unexpected repo type; want: %s, have %s.", wantType, haveType)
}
return nil
} }
...@@ -20,41 +20,56 @@ import ( ...@@ -20,41 +20,56 @@ import (
"testing" "testing"
) )
func TestValidURL(t *testing.T) { var (
var wantName = "wantName" TestRepoName = "kubernetes-charts-testing"
var wantType = "wantType" TestRepoBucket = TestRepoName
var validURL = "http://valid/url" TestRepoURL = "gs://" + TestRepoBucket
var wantFormat = "wantFormat" TestRepoType = GCSRepoType
TestRepoFormat = GCSRepoFormat
TestRepoCredentialName = "default"
)
tr, err := newRepo(wantName, validURL, wantFormat, wantType) func TestValidRepoURL(t *testing.T) {
tr, err := NewRepo(TestRepoName, TestRepoURL, TestRepoCredentialName, string(TestRepoFormat), string(TestRepoType))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
haveName := tr.GetRepoName() if err := validateRepo(tr, TestRepoName, TestRepoURL, TestRepoCredentialName, TestRepoFormat, TestRepoType); err != nil {
if haveName != wantName { t.Fatal(err)
t.Fatalf("unexpected repo name; want: %s, have %s.", wantName, haveName) }
}
func TestInvalidRepoName(t *testing.T) {
_, err := newRepo("", TestRepoURL, TestRepoCredentialName, TestRepoFormat, TestRepoType)
if err == nil {
t.Fatalf("expected error did not occur for invalid name")
} }
}
haveType := string(tr.GetRepoType()) func TestInvalidRepoURL(t *testing.T) {
if haveType != wantType { _, err := newRepo(TestRepoName, "%:invalid&url:%", TestRepoCredentialName, TestRepoFormat, TestRepoType)
t.Fatalf("unexpected repo type; want: %s, have %s.", wantType, haveType) if err == nil {
t.Fatalf("expected error did not occur for invalid URL")
} }
}
haveURL := tr.GetRepoURL() func TestDefaultCredentialName(t *testing.T) {
if haveURL != validURL { tr, err := newRepo(TestRepoName, TestRepoURL, "", TestRepoFormat, TestRepoType)
t.Fatalf("unexpected repo url; want: %s, have %s.", validURL, haveURL) if err != nil {
t.Fatalf("cannot create repo using default credential name")
} }
haveFormat := string(tr.GetRepoFormat()) TestRepoCredentialName := "default"
if haveFormat != wantFormat { haveCredentialName := tr.GetCredentialName()
t.Fatalf("unexpected repo format; want: %s, have %s.", wantFormat, haveFormat) if haveCredentialName != TestRepoCredentialName {
t.Fatalf("unexpected credential name; want: %s, have %s.", TestRepoCredentialName, haveCredentialName)
} }
} }
func TestInvalidURL(t *testing.T) { func TestInvalidRepoFormat(t *testing.T) {
_, err := newRepo("testName", "%:invalid&url:%", "testFormat", "testType") _, err := newRepo(TestRepoName, TestRepoURL, TestRepoCredentialName, "", TestRepoType)
if err == nil { if err == nil {
t.Fatalf("expected error did not occur for invalid URL") t.Fatalf("expected error did not occur for invalid format")
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment