Add repo package

pull/406/head
jackgr 9 years ago
parent 7a3fd09c25
commit 54abaf7eda

@ -227,10 +227,10 @@ type Registry struct {
CredentialName string `json:"credentialname,omitempty"` // Name of the credential to use
}
// RegistryType defines the technology that implements the registry
// RegistryType defines the technology that implements a registry.
type RegistryType string
// Constants that identify the supported registry layouts.
// Constants that identify the supported registry types.
const (
GithubRegistryType RegistryType = "github"
GCSRegistryType RegistryType = "gcs"
@ -256,6 +256,34 @@ const (
OneLevelRegistry RegistryFormat = "onelevel"
)
// RepoType defines the technology that implements a repository.
type RepoType string
// Constants that identify the supported repository types.
const (
GCSRepoType RepoType = "gcs"
)
// RepoFormat is a semi-colon delimited string that describes the format
// of a repository.
type RepoFormat string
const (
// Versioning.
// VersionedRepo identifies a versioned repository, where types appear under versions.
VersionedRepo RepoFormat = "versioned"
// UnversionedRepo identifies an unversioned repository, where types appear under their names.
UnversionedRepo RepoFormat = "unversioned"
// Organization.
// CollectionRepo identfies a collection repository, where types are grouped into collections.
CollectionRepo RepoFormat = "collection"
// OneLevelRepo identifies a one level repository, where all types appear at the top level.
OneLevelRepo RepoFormat = "onelevel"
)
// RegistryService maintains a set of registries that defines the scope of all
// registry based operations, such as search and type resolution.
type RegistryService interface {

@ -0,0 +1,132 @@
/*
Copyright 2015 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 repo
import (
"github.com/kubernetes/helm/pkg/chart"
"github.com/kubernetes/helm/pkg/common"
"github.com/kubernetes/helm/pkg/util"
storage "google.golang.org/api/storage/v1"
"fmt"
"net/http"
"regexp"
)
// GCSRepo implements the ObjectStorageRepo interface
// for Google Cloud Storage.
//
// A GCSRepo root must be a directory that contains all the available charts.
type GCSRepo struct {
chartRepo // A GCSRepo is a chartRepo
bucket string
credentialName string
httpClient *http.Client
service *storage.Service
}
// URLFormatMatcher matches the GCS URL format (gs:).
var URLFormatMatcher = regexp.MustCompile("gs://(.*)")
var GCSRepoFormat = common.RepoFormat(fmt.Sprintf("%s;%s", common.VersionedRepo, common.OneLevelRepo))
// NewGCSRepo creates a GCS repository.
func NewGCSRepo(name, URL string, httpClient *http.Client) (*GCSRepo, error) {
m := URLFormatMatcher.FindStringSubmatch(URL)
if len(m) != 2 {
return nil, fmt.Errorf("URL must be of the form gs://<bucket>, was %s", URL)
}
cr, err := newRepo(name, URL, string(GCSRepoFormat), string(common.GCSRepoType))
if err != nil {
return nil, err
}
if httpClient == nil {
httpClient = http.DefaultClient
}
gs, err := storage.New(httpClient)
if err != nil {
return nil, fmt.Errorf("cannot create storage service for %s: %s", URL, err)
}
result := &GCSRepo{
chartRepo: *cr,
httpClient: httpClient,
service: gs,
bucket: m[1],
}
return result, nil
}
// GetBucket returns the repository bucket.
func (g *GCSRepo) GetBucket() string {
return g.bucket
}
// ListCharts lists charts in this chart repository whose string values conform to the
// supplied regular expression, or all charts, if the regular expression is nil.
func (g *GCSRepo) ListCharts(regex *regexp.Regexp) ([]string, error) {
// List all files in the bucket/prefix that contain the
charts := []string{}
// List all objects in a bucket using pagination
pageToken := ""
for {
call := g.service.Objects.List(g.bucket)
call.Delimiter("/")
if pageToken != "" {
call = call.PageToken(pageToken)
}
res, err := call.Do()
if err != nil {
return nil, err
}
for _, object := range res.Items {
// Charts should be named bucket/chart-X.Y.Z.tgz, so tease apart the version here
m := ChartNameMatcher.FindStringSubmatch(object.Name)
if len(m) != 3 {
continue
}
charts = append(charts, object.Name)
}
if pageToken = res.NextPageToken; pageToken == "" {
break
}
}
return charts, nil
}
// TODO: Implement GetChart.
// GetChart retrieves, unpacks and returns a chart by name.
func (g *GCSRepo) GetChart(name string) (*chart.Chart, error) {
return nil, fmt.Errorf("not implemented: GCSRepo.GetChart")
}
// TODO: Remove GetShortURL when no longer needed.
// GetShortURL returns the URL without the scheme.
func (g GCSRepo) GetShortURL() string {
return util.TrimURLScheme(g.URL)
}

@ -0,0 +1,52 @@
/*
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 repo
import (
"github.com/kubernetes/helm/pkg/common"
"testing"
)
func TestValidGSURL(t *testing.T) {
var validURL = "gs://bucket"
tr, err := NewGCSRepo("testName", validURL, nil)
if err != nil {
t.Fatal(err)
}
wantType := common.GCSRepoType
haveType := tr.GetRepoType()
if haveType != wantType {
t.Fatalf("unexpected repo type; want: %s, have %s.", wantType, haveType)
}
wantFormat := GCSRepoFormat
haveFormat := tr.GetRepoFormat()
if haveFormat != wantFormat {
t.Fatalf("unexpected repo format; want: %s, have %s.", wantFormat, haveFormat)
}
}
func TestInvalidGSURL(t *testing.T) {
var invalidURL = "https://bucket"
_, err := NewGCSRepo("testName", invalidURL, nil)
if err == nil {
t.Fatalf("expected error did not occur for invalid URL")
}
}

@ -0,0 +1,99 @@
/*
Copyright 2015 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 repo
import (
"github.com/kubernetes/helm/pkg/chart"
"github.com/kubernetes/helm/pkg/common"
"fmt"
"net/url"
"regexp"
)
// ChartRepo abstracts a place that holds charts, which can be
// used in a Deployment Manager configuration. There can be multiple
// ChartRepo implementations.
type ChartRepo interface {
// GetRepoName returns the name of this ChartRepo.
GetRepoName() string
// GetRepoType returns the type of this repo.
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 {
Name string `json:"name,omitempty"` // The name of this ChartRepo
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
var ChartNameMatcher = regexp.MustCompile("(.*)-(.*).tgz")
func newRepo(name, URL, format, t string) (*chartRepo, error) {
_, err := url.Parse(URL)
if err != nil {
return nil, fmt.Errorf("invalid URL (%s): %s", URL, err)
}
result := &chartRepo{
Name: name,
URL: URL,
Format: common.RepoFormat(format),
Type: common.RepoType(t),
}
return result, nil
}
// GetRepoName returns the name of this ChartRepo.
func (cr *chartRepo) GetRepoName() string {
return cr.Name
}
// GetRepoType returns the type of this repo.
func (cr *chartRepo) GetRepoType() common.RepoType {
return cr.Type
}
// GetRepoURL returns the URL to the root of this ChartRepo.
func (cr *chartRepo) GetRepoURL() string {
return cr.URL
}
// GetRepoFormat returns the format of this ChartRepo.
func (cr *chartRepo) GetRepoFormat() common.RepoFormat {
return cr.Format
}

@ -0,0 +1,60 @@
/*
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 repo
import (
"testing"
)
func TestValidURL(t *testing.T) {
var wantName = "wantName"
var wantType = "wantType"
var validURL = "http://valid/url"
var wantFormat = "wantFormat"
tr, err := newRepo(wantName, validURL, wantFormat, wantType)
if err != nil {
t.Fatal(err)
}
haveName := tr.GetRepoName()
if haveName != wantName {
t.Fatalf("unexpected repo name; want: %s, have %s.", wantName, haveName)
}
haveType := string(tr.GetRepoType())
if haveType != wantType {
t.Fatalf("unexpected repo type; want: %s, have %s.", wantType, haveType)
}
haveURL := tr.GetRepoURL()
if haveURL != validURL {
t.Fatalf("unexpected repo url; want: %s, have %s.", validURL, haveURL)
}
haveFormat := string(tr.GetRepoFormat())
if haveFormat != wantFormat {
t.Fatalf("unexpected repo format; want: %s, have %s.", wantFormat, haveFormat)
}
}
func TestInvalidURL(t *testing.T) {
_, err := newRepo("testName", "%:invalid&url:%", "testFormat", "testType")
if err == nil {
t.Fatalf("expected error did not occur for invalid URL")
}
}
Loading…
Cancel
Save