mirror of https://github.com/helm/helm
parent
7a3fd09c25
commit
54abaf7eda
@ -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…
Reference in new issue