mirror of https://github.com/helm/helm
parent
7fd7beba57
commit
94f78a5824
@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FilebasedCredentialProvider provides credentials for registries.
|
|
||||||
type FilebasedCredentialProvider struct {
|
|
||||||
// Actual backing store
|
|
||||||
backingCredentialProvider common.CredentialProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamedRegistryCredential associates a name with a RegistryCredential.
|
|
||||||
type NamedRegistryCredential struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
common.RegistryCredential
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFilebasedCredentialProvider creates a file based credential provider.
|
|
||||||
func NewFilebasedCredentialProvider(filename string) (common.CredentialProvider, error) {
|
|
||||||
icp := NewInmemCredentialProvider()
|
|
||||||
c, err := readCredentialsFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return &FilebasedCredentialProvider{}, err
|
|
||||||
}
|
|
||||||
for _, nc := range c {
|
|
||||||
log.Printf("Adding credential %s", nc.Name)
|
|
||||||
icp.SetCredential(nc.Name, &nc.RegistryCredential)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &FilebasedCredentialProvider{icp}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readCredentialsFile(filename string) ([]NamedRegistryCredential, error) {
|
|
||||||
bytes, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return []NamedRegistryCredential{}, err
|
|
||||||
}
|
|
||||||
return parseCredentials(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseCredentials(bytes []byte) ([]NamedRegistryCredential, error) {
|
|
||||||
r := []NamedRegistryCredential{}
|
|
||||||
if err := yaml.Unmarshal(bytes, &r); err != nil {
|
|
||||||
return []NamedRegistryCredential{}, fmt.Errorf("cannot unmarshal credentials file (%#v)", err)
|
|
||||||
}
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCredential returns a credential by name.
|
|
||||||
func (fcp *FilebasedCredentialProvider) GetCredential(name string) (*common.RegistryCredential, error) {
|
|
||||||
return fcp.backingCredentialProvider.GetCredential(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCredential sets a credential by name.
|
|
||||||
func (fcp *FilebasedCredentialProvider) SetCredential(name string, credential *common.RegistryCredential) error {
|
|
||||||
return fmt.Errorf("SetCredential operation not supported with FilebasedCredentialProvider")
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
var filename = "./test/test_credentials_file.yaml"
|
|
||||||
|
|
||||||
type filebasedTestCase struct {
|
|
||||||
name string
|
|
||||||
exp *common.RegistryCredential
|
|
||||||
expErr error
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotExistFilebased(t *testing.T) {
|
|
||||||
cp, err := NewFilebasedCredentialProvider(filename)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create a new FilebasedCredentialProvider %s : %v", filename, err)
|
|
||||||
}
|
|
||||||
tc := &testCase{"nonexistent", nil, createMissingError("nonexistent")}
|
|
||||||
testGetCredential(t, cp, tc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetApiTokenFilebased(t *testing.T) {
|
|
||||||
cp, err := NewFilebasedCredentialProvider(filename)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create a new FilebasedCredentialProvider %s : %v", filename, err)
|
|
||||||
}
|
|
||||||
tc := &testCase{"test1", &common.RegistryCredential{APIToken: "token"}, nil}
|
|
||||||
testGetCredential(t, cp, tc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetAndGetBasicAuthFilebased(t *testing.T) {
|
|
||||||
cp, err := NewFilebasedCredentialProvider(filename)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create a new FilebasedCredentialProvider %s : %v", filename, err)
|
|
||||||
}
|
|
||||||
tc := &testCase{"test2",
|
|
||||||
&common.RegistryCredential{
|
|
||||||
BasicAuth: common.BasicAuthCredential{Username: "user", Password: "password"}}, nil}
|
|
||||||
testGetCredential(t, cp, tc)
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
"github.com/kubernetes/helm/pkg/util"
|
|
||||||
|
|
||||||
// "golang.org/x/net/context"
|
|
||||||
// "golang.org/x/oauth2/google"
|
|
||||||
storage "google.golang.org/api/storage/v1"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GCSRegistry implements the ObbectStorageRegistry interface and implements a
|
|
||||||
// Deployment Manager templates registry.
|
|
||||||
//
|
|
||||||
// A registry root must be a directory that contains all the available charts,
|
|
||||||
// one or two files per template.
|
|
||||||
// name-version.tgz
|
|
||||||
// name-version.prov
|
|
||||||
type GCSRegistry struct {
|
|
||||||
name string
|
|
||||||
shortURL string
|
|
||||||
bucket string
|
|
||||||
format common.RegistryFormat
|
|
||||||
credentialName string
|
|
||||||
|
|
||||||
httpClient *http.Client
|
|
||||||
service *storage.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
// RE for GCS storage
|
|
||||||
|
|
||||||
// ChartFormatMatcher matches the chart name format
|
|
||||||
var ChartFormatMatcher = regexp.MustCompile("(.*)-(.*).tgz")
|
|
||||||
|
|
||||||
// URLFormatMatcher matches the GCS URL format (gs:).
|
|
||||||
var URLFormatMatcher = regexp.MustCompile("gs://(.*)")
|
|
||||||
|
|
||||||
// NewGCSRegistry creates a GCS registry.
|
|
||||||
func NewGCSRegistry(name, shortURL string, httpClient *http.Client, gcsService *storage.Service) (*GCSRegistry, error) {
|
|
||||||
format := fmt.Sprintf("%s;%s", common.VersionedRegistry, common.OneLevelRegistry)
|
|
||||||
trimmed := util.TrimURLScheme(shortURL)
|
|
||||||
m := URLFormatMatcher.FindStringSubmatch(shortURL)
|
|
||||||
if len(m) != 2 {
|
|
||||||
return nil, fmt.Errorf("URL must be of the form gs://<bucket> was: %s", shortURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &GCSRegistry{
|
|
||||||
name: name,
|
|
||||||
shortURL: trimmed,
|
|
||||||
format: common.RegistryFormat(format),
|
|
||||||
httpClient: httpClient,
|
|
||||||
service: gcsService,
|
|
||||||
bucket: m[1],
|
|
||||||
},
|
|
||||||
nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryName returns the name of the registry.
|
|
||||||
func (g GCSRegistry) GetRegistryName() string {
|
|
||||||
return g.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBucket returns the registry bucket.
|
|
||||||
func (g GCSRegistry) GetBucket() string {
|
|
||||||
return g.bucket
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryType returns the registry type.
|
|
||||||
func (g GCSRegistry) GetRegistryType() common.RegistryType {
|
|
||||||
return common.GCSRegistryType
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListTypes lists types in this registry whose string values conform to the
|
|
||||||
// supplied regular expression, or all types, if the regular expression is nil.
|
|
||||||
func (g GCSRegistry) ListTypes(regex *regexp.Regexp) ([]Type, error) {
|
|
||||||
// List all files in the bucket/prefix that contain the
|
|
||||||
types := []Type{}
|
|
||||||
|
|
||||||
// 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 []Type{}, err
|
|
||||||
}
|
|
||||||
for _, object := range res.Items {
|
|
||||||
// Charts should be named bucket/chart-X.Y.Z.tgz, so tease apart the version here
|
|
||||||
m := ChartFormatMatcher.FindStringSubmatch(object.Name)
|
|
||||||
if len(m) != 3 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := NewType("", m[1], m[2])
|
|
||||||
if err != nil {
|
|
||||||
return []Type{}, fmt.Errorf("can't create a type type at path %#v", err)
|
|
||||||
}
|
|
||||||
types = append(types, t)
|
|
||||||
}
|
|
||||||
if pageToken = res.NextPageToken; pageToken == "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return types, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryFormat returns the registry format.
|
|
||||||
func (g GCSRegistry) GetRegistryFormat() common.RegistryFormat {
|
|
||||||
return common.CollectionRegistry
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryShortURL returns the short URL for the registry.
|
|
||||||
func (g GCSRegistry) GetRegistryShortURL() string {
|
|
||||||
return g.shortURL
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDownloadURLs fetches the download URLs for a given Chart
|
|
||||||
func (g GCSRegistry) GetDownloadURLs(t Type) ([]*url.URL, error) {
|
|
||||||
call := g.service.Objects.List(g.bucket)
|
|
||||||
call.Delimiter("/")
|
|
||||||
call.Prefix(t.String())
|
|
||||||
res, err := call.Do()
|
|
||||||
ret := []*url.URL{}
|
|
||||||
if err != nil {
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
for _, object := range res.Items {
|
|
||||||
log.Printf("Found: %s", object.Name)
|
|
||||||
u, err := url.Parse(object.MediaLink)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot parse URL from %s: %s", object.MediaLink, err)
|
|
||||||
}
|
|
||||||
ret = append(ret, u)
|
|
||||||
}
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do performs an HTTP operation on the receiver's httpClient.
|
|
||||||
func (g GCSRegistry) Do(req *http.Request) (resp *http.Response, err error) {
|
|
||||||
return g.httpClient.Do(req)
|
|
||||||
}
|
|
@ -1,159 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/google/go-github/github"
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GithubPackageRegistry implements the Registry interface that talks to github and
|
|
||||||
// expects packages in helm format without versioning and no collection in the path.
|
|
||||||
// Format of the directory for a type is like so:
|
|
||||||
// package/
|
|
||||||
// Chart.yaml
|
|
||||||
// manifests/
|
|
||||||
// foo.yaml
|
|
||||||
// bar.yaml
|
|
||||||
// ...
|
|
||||||
type GithubPackageRegistry struct {
|
|
||||||
githubRegistry
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGithubPackageRegistry creates a GithubPackageRegistry.
|
|
||||||
func NewGithubPackageRegistry(name, shortURL string, service GithubRepositoryService, httpClient *http.Client, client *github.Client) (*GithubPackageRegistry, error) {
|
|
||||||
format := fmt.Sprintf("%s;%s", common.UnversionedRegistry, common.OneLevelRegistry)
|
|
||||||
if service == nil {
|
|
||||||
if client == nil {
|
|
||||||
service = github.NewClient(nil).Repositories
|
|
||||||
} else {
|
|
||||||
service = client.Repositories
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gr, err := newGithubRegistry(name, shortURL, common.RegistryFormat(format), httpClient, service)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &GithubPackageRegistry{githubRegistry: *gr}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListTypes lists types in this registry whose string values conform to the
|
|
||||||
// supplied regular expression, or all types, if the regular expression is nil.
|
|
||||||
func (g GithubPackageRegistry) ListTypes(regex *regexp.Regexp) ([]Type, error) {
|
|
||||||
// Just list all the types at the top level.
|
|
||||||
types, err := g.getDirs("")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to list templates: %v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var retTypes []Type
|
|
||||||
for _, t := range types {
|
|
||||||
// Check to see if there's a Chart.yaml file in the directory
|
|
||||||
_, dc, _, err := g.service.GetContents(g.owner, g.repository, t, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to list package files at path: %s: %v", t, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, f := range dc {
|
|
||||||
if *f.Type == "file" && *f.Name == "Chart.yaml" {
|
|
||||||
retTypes = append(retTypes, Type{Name: t})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if regex != nil {
|
|
||||||
var matchTypes []Type
|
|
||||||
for _, retType := range retTypes {
|
|
||||||
if regex.MatchString(retType.String()) {
|
|
||||||
matchTypes = append(matchTypes, retType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchTypes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return retTypes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDownloadURLs fetches the download URLs for a given Type.
|
|
||||||
func (g GithubPackageRegistry) GetDownloadURLs(t Type) ([]*url.URL, error) {
|
|
||||||
path, err := g.MakeRepositoryPath(t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, dc, _, err := g.service.GetContents(g.owner, g.repository, path, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to list package files at path: %s: %v", path, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadURLs := []*url.URL{}
|
|
||||||
for _, f := range dc {
|
|
||||||
if *f.Type == "file" {
|
|
||||||
if strings.HasSuffix(*f.Name, ".yaml") {
|
|
||||||
u, err := url.Parse(*f.DownloadURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot parse URL from %s: %s", *f.DownloadURL, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadURLs = append(downloadURLs, u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return downloadURLs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g GithubPackageRegistry) getDirs(dir string) ([]string, error) {
|
|
||||||
_, dc, _, err := g.service.GetContents(g.owner, g.repository, dir, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to get contents at path: %s: %v", dir, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var dirs []string
|
|
||||||
for _, entry := range dc {
|
|
||||||
if *entry.Type == "dir" {
|
|
||||||
dirs = append(dirs, *entry.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dirs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRepositoryPath constructs a github path to a given type based on a repository, and type name.
|
|
||||||
// The returned repository path will be of the form:
|
|
||||||
// Type.Name/manifests
|
|
||||||
func (g GithubPackageRegistry) MakeRepositoryPath(t Type) (string, error) {
|
|
||||||
// Construct the return path
|
|
||||||
return t.Name + "/manifests", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do performs an HTTP operation on the receiver's httpClient.
|
|
||||||
func (g GithubPackageRegistry) Do(req *http.Request) (resp *http.Response, err error) {
|
|
||||||
return g.httpClient.Do(req)
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/google/go-github/github"
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
"github.com/kubernetes/helm/pkg/util"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// githubRegistry is the base class for the Registry interface and talks to github. Actual implementations are
|
|
||||||
// in GithubPackageRegistry and GithubTemplateRegistry.
|
|
||||||
// The registry short URL and format determine how types are laid out in the
|
|
||||||
// registry.
|
|
||||||
type githubRegistry struct {
|
|
||||||
name string
|
|
||||||
shortURL string
|
|
||||||
owner string
|
|
||||||
repository string
|
|
||||||
path string
|
|
||||||
format common.RegistryFormat
|
|
||||||
credentialName string
|
|
||||||
service GithubRepositoryService
|
|
||||||
httpClient *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
// GithubRepositoryService defines the interface that's defined in github.com/go-github/repos_contents.go GetContents method.
|
|
||||||
type GithubRepositoryService interface {
|
|
||||||
GetContents(
|
|
||||||
owner, repo, path string,
|
|
||||||
opt *github.RepositoryContentGetOptions,
|
|
||||||
) (
|
|
||||||
fileContent *github.RepositoryContent,
|
|
||||||
directoryContent []*github.RepositoryContent,
|
|
||||||
resp *github.Response,
|
|
||||||
err error,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newGithubRegistry creates a githubRegistry.
|
|
||||||
func newGithubRegistry(name, shortURL string, format common.RegistryFormat, httpClient *http.Client, service GithubRepositoryService) (*githubRegistry, error) {
|
|
||||||
trimmed := util.TrimURLScheme(shortURL)
|
|
||||||
owner, repository, path, err := parseGithubShortURL(trimmed)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot create Github template registry %s: %s", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &githubRegistry{
|
|
||||||
name: name,
|
|
||||||
shortURL: trimmed,
|
|
||||||
owner: owner,
|
|
||||||
repository: repository,
|
|
||||||
path: path,
|
|
||||||
format: format,
|
|
||||||
service: service,
|
|
||||||
httpClient: httpClient,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseGithubShortURL(shortURL string) (string, string, string, error) {
|
|
||||||
if !strings.HasPrefix(shortURL, "github.com/") {
|
|
||||||
return "", "", "", fmt.Errorf("invalid Github short URL: %s", shortURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
tPath := strings.TrimPrefix(shortURL, "github.com/")
|
|
||||||
parts := strings.Split(tPath, "/")
|
|
||||||
|
|
||||||
// Handle the case where there's no path after owner and repository.
|
|
||||||
if len(parts) == 2 {
|
|
||||||
return parts[0], parts[1], "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the case where there's a path after owner and repository.
|
|
||||||
if len(parts) == 3 {
|
|
||||||
return parts[0], parts[1], parts[2], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", "", "", fmt.Errorf("invalid Github short URL: %s", shortURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryName returns the name of this registry
|
|
||||||
func (g githubRegistry) GetRegistryName() string {
|
|
||||||
return g.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryType returns the type of this registry.
|
|
||||||
func (g githubRegistry) GetRegistryType() common.RegistryType {
|
|
||||||
return common.GithubRegistryType
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryShortURL returns the short URL for this registry.
|
|
||||||
func (g githubRegistry) GetRegistryShortURL() string {
|
|
||||||
return g.shortURL
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryFormat returns the format of this registry.
|
|
||||||
func (g githubRegistry) GetRegistryFormat() common.RegistryFormat {
|
|
||||||
return g.format
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryOwner returns the owner name for this registry
|
|
||||||
func (g githubRegistry) GetRegistryOwner() string {
|
|
||||||
return g.owner
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryRepository returns the repository name for this registry.
|
|
||||||
func (g githubRegistry) GetRegistryRepository() string {
|
|
||||||
return g.repository
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryName returns the name of this registry
|
|
||||||
func (g githubRegistry) GetRegistryPath() string {
|
|
||||||
return g.path
|
|
||||||
}
|
|
@ -1,220 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/google/go-github/github"
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GithubTemplateRegistry implements the Registry interface and implements a
|
|
||||||
// Deployment Manager templates registry.
|
|
||||||
// A registry root must be a directory that contains all the available templates,
|
|
||||||
// one directory per template. Each template directory then contains version
|
|
||||||
// directories, each of which in turn contains all the files necessary for that
|
|
||||||
// version of the template.
|
|
||||||
//
|
|
||||||
// For example, a template registry containing two versions of redis
|
|
||||||
// (implemented in jinja), and one version of replicatedservice (implemented
|
|
||||||
// in python) would have a directory structure that looks something like this:
|
|
||||||
// qualifier [optional] prefix to a virtual root within the repository.
|
|
||||||
// /redis
|
|
||||||
// /v1
|
|
||||||
// redis.jinja
|
|
||||||
// redis.jinja.schema
|
|
||||||
// /v2
|
|
||||||
// redis.jinja
|
|
||||||
// redis.jinja.schema
|
|
||||||
// /replicatedservice
|
|
||||||
// /v1
|
|
||||||
// replicatedservice.python
|
|
||||||
// replicatedservice.python.schema
|
|
||||||
type GithubTemplateRegistry struct {
|
|
||||||
githubRegistry
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGithubTemplateRegistry creates a GithubTemplateRegistry.
|
|
||||||
func NewGithubTemplateRegistry(name, shortURL string, service GithubRepositoryService, httpClient *http.Client, client *github.Client) (*GithubTemplateRegistry, error) {
|
|
||||||
format := fmt.Sprintf("%s;%s", common.VersionedRegistry, common.CollectionRegistry)
|
|
||||||
if service == nil {
|
|
||||||
service = client.Repositories
|
|
||||||
}
|
|
||||||
|
|
||||||
gr, err := newGithubRegistry(name, shortURL, common.RegistryFormat(format), httpClient, service)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &GithubTemplateRegistry{githubRegistry: *gr}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListTypes lists types in this registry whose string values conform to the
|
|
||||||
// supplied regular expression, or all types, if the regular expression is nil.
|
|
||||||
func (g GithubTemplateRegistry) ListTypes(regex *regexp.Regexp) ([]Type, error) {
|
|
||||||
// First list all the collections at the top level.
|
|
||||||
collections, err := g.getDirs("")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("cannot list qualifiers: %v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var retTypes []Type
|
|
||||||
for _, c := range collections {
|
|
||||||
// Then we need to fetch the versions (directories for this type)
|
|
||||||
types, err := g.getDirs(c)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("cannot fetch types for collection: %s", c)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, t := range types {
|
|
||||||
path := c + "/" + t
|
|
||||||
// Then we need to fetch the versions (directories for this type)
|
|
||||||
versions, err := g.getDirs(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("cannot fetch versions at path %s", path)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range versions {
|
|
||||||
tt, err := NewType(c, t, v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("malformed type at path %s", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
retTypes = append(retTypes, tt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if regex != nil {
|
|
||||||
var matchTypes []Type
|
|
||||||
for _, retType := range retTypes {
|
|
||||||
if regex.MatchString(retType.String()) {
|
|
||||||
matchTypes = append(matchTypes, retType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchTypes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return retTypes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDownloadURLs fetches the download URLs for a given Type and checks for existence of a schema file.
|
|
||||||
func (g GithubTemplateRegistry) GetDownloadURLs(t Type) ([]*url.URL, error) {
|
|
||||||
path, err := g.MakeRepositoryPath(t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, dc, _, err := g.service.GetContents(g.owner, g.repository, path, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot list versions at path %s: %v", path, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var downloadURL, typeName, schemaName string
|
|
||||||
for _, f := range dc {
|
|
||||||
if *f.Type == "file" {
|
|
||||||
if *f.Name == t.Name+".jinja" || *f.Name == t.Name+".py" {
|
|
||||||
typeName = *f.Name
|
|
||||||
downloadURL = *f.DownloadURL
|
|
||||||
}
|
|
||||||
if *f.Name == t.Name+".jinja.schema" || *f.Name == t.Name+".py.schema" {
|
|
||||||
schemaName = *f.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if downloadURL == "" {
|
|
||||||
return nil, fmt.Errorf("cannot find type %s", t.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if schemaName != typeName+".schema" {
|
|
||||||
return nil, fmt.Errorf("cannot find schema for %s, expected %s", t.String(), typeName+".schema")
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := url.Parse(downloadURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot parse URL from %s: %s", downloadURL, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return []*url.URL{result}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g GithubTemplateRegistry) getDirs(dir string) ([]string, error) {
|
|
||||||
var path = g.path
|
|
||||||
if dir != "" {
|
|
||||||
path = g.path + "/" + dir
|
|
||||||
}
|
|
||||||
|
|
||||||
_, dc, _, err := g.service.GetContents(g.owner, g.repository, path, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to get contents at path: %s: %v", path, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var dirs []string
|
|
||||||
for _, entry := range dc {
|
|
||||||
if *entry.Type == "dir" {
|
|
||||||
dirs = append(dirs, *entry.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dirs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g GithubTemplateRegistry) mapCollection(collection string) (string, error) {
|
|
||||||
if strings.ContainsAny(collection, "/") {
|
|
||||||
return "", fmt.Errorf("collection must not contain slashes, got %s", collection)
|
|
||||||
}
|
|
||||||
// TODO(vaikas): Implement lookup from the root metadata file to map collection to a path
|
|
||||||
return collection, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRepositoryPath constructs a github path to a given type based on a repository, and type name and version.
|
|
||||||
// The returned repository path will be of the form:
|
|
||||||
// [GithubTemplateRegistry.path/][Type.Collection]/Type.Name/Type.Version
|
|
||||||
// Type.Collection will be mapped using mapCollection in the future, for now it's a straight
|
|
||||||
// 1:1 mapping (if given)
|
|
||||||
func (g GithubTemplateRegistry) MakeRepositoryPath(t Type) (string, error) {
|
|
||||||
// First map the collection
|
|
||||||
collection, err := g.mapCollection(t.Collection)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// Construct the return path
|
|
||||||
p := ""
|
|
||||||
if len(g.path) > 0 {
|
|
||||||
p += g.path + "/"
|
|
||||||
}
|
|
||||||
if len(collection) > 0 {
|
|
||||||
p += collection + "/"
|
|
||||||
}
|
|
||||||
return p + t.Name + "/" + t.GetVersion(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do performs an HTTP operation on the receiver's httpClient.
|
|
||||||
func (g GithubTemplateRegistry) Do(req *http.Request) (resp *http.Response, err error) {
|
|
||||||
return g.httpClient.Do(req)
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InmemCredentialProvider is a memory based credential provider.
|
|
||||||
type InmemCredentialProvider struct {
|
|
||||||
credentials map[string]*common.RegistryCredential
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInmemCredentialProvider creates a new memory based credential provider.
|
|
||||||
func NewInmemCredentialProvider() common.CredentialProvider {
|
|
||||||
return &InmemCredentialProvider{credentials: make(map[string]*common.RegistryCredential)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCredential returns a credential by name.
|
|
||||||
func (fcp *InmemCredentialProvider) GetCredential(name string) (*common.RegistryCredential, error) {
|
|
||||||
if val, ok := fcp.credentials[name]; ok {
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("no such credential : %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCredential sets a credential by name.
|
|
||||||
func (fcp *InmemCredentialProvider) SetCredential(name string, credential *common.RegistryCredential) error {
|
|
||||||
fcp.credentials[name] = &common.RegistryCredential{APIToken: credential.APIToken, BasicAuth: credential.BasicAuth, ServiceAccount: credential.ServiceAccount}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testCase struct {
|
|
||||||
name string
|
|
||||||
exp *common.RegistryCredential
|
|
||||||
expErr error
|
|
||||||
}
|
|
||||||
|
|
||||||
func createMissingError(name string) error {
|
|
||||||
return fmt.Errorf("no such credential : %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testGetCredential(t *testing.T, cp common.CredentialProvider, tc *testCase) {
|
|
||||||
actual, actualErr := cp.GetCredential(tc.name)
|
|
||||||
if !reflect.DeepEqual(actual, tc.exp) {
|
|
||||||
t.Fatalf("failed on: %s : expected %#v but got %#v", tc.name, tc.exp, actual)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(actualErr, tc.expErr) {
|
|
||||||
t.Fatalf("failed on: %s : expected error %#v but got %#v", tc.name, tc.expErr, actualErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifySetAndGetCredential(t *testing.T, cp common.CredentialProvider, tc *testCase) {
|
|
||||||
err := cp.SetCredential(tc.name, tc.exp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to SetCredential on %s : %v", tc.name, err)
|
|
||||||
}
|
|
||||||
testGetCredential(t, cp, tc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotExist(t *testing.T) {
|
|
||||||
cp := NewInmemCredentialProvider()
|
|
||||||
tc := &testCase{"nonexistent", nil, createMissingError("nonexistent")}
|
|
||||||
testGetCredential(t, cp, tc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetAndGetApiToken(t *testing.T) {
|
|
||||||
cp := NewInmemCredentialProvider()
|
|
||||||
tc := &testCase{"testcredential", &common.RegistryCredential{APIToken: "some token here"}, nil}
|
|
||||||
verifySetAndGetCredential(t, cp, tc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetAndGetBasicAuth(t *testing.T) {
|
|
||||||
cp := NewInmemCredentialProvider()
|
|
||||||
tc := &testCase{"testcredential",
|
|
||||||
&common.RegistryCredential{
|
|
||||||
BasicAuth: common.BasicAuthCredential{Username: "user", Password: "pass"}}, nil}
|
|
||||||
verifySetAndGetCredential(t, cp, tc)
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
"github.com/kubernetes/helm/pkg/util"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type inmemRegistryService struct {
|
|
||||||
registries map[string]*common.Registry
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInmemRegistryService returns a new memory based registry service.
|
|
||||||
func NewInmemRegistryService() common.RegistryService {
|
|
||||||
rs := &inmemRegistryService{
|
|
||||||
registries: make(map[string]*common.Registry),
|
|
||||||
}
|
|
||||||
|
|
||||||
pFormat := fmt.Sprintf("%s;%s", common.UnversionedRegistry, common.OneLevelRegistry)
|
|
||||||
rs.Create(&common.Registry{
|
|
||||||
Name: "charts",
|
|
||||||
Type: common.GithubRegistryType,
|
|
||||||
URL: "github.com/helm/charts",
|
|
||||||
Format: common.RegistryFormat(pFormat),
|
|
||||||
CredentialName: "default",
|
|
||||||
})
|
|
||||||
|
|
||||||
tFormat := fmt.Sprintf("%s;%s", common.VersionedRegistry, common.CollectionRegistry)
|
|
||||||
rs.Create(&common.Registry{
|
|
||||||
Name: "application-dm-templates",
|
|
||||||
Type: common.GithubRegistryType,
|
|
||||||
URL: "github.com/kubernetes/application-dm-templates",
|
|
||||||
Format: common.RegistryFormat(tFormat),
|
|
||||||
CredentialName: "default",
|
|
||||||
})
|
|
||||||
|
|
||||||
return rs
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns the list of known registries.
|
|
||||||
func (rs *inmemRegistryService) List() ([]*common.Registry, error) {
|
|
||||||
ret := []*common.Registry{}
|
|
||||||
for _, r := range rs.registries {
|
|
||||||
ret = append(ret, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create creates a registry.
|
|
||||||
func (rs *inmemRegistryService) Create(registry *common.Registry) error {
|
|
||||||
rs.registries[registry.Name] = registry
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns a registry with a given name.
|
|
||||||
func (rs *inmemRegistryService) Get(name string) (*common.Registry, error) {
|
|
||||||
r, ok := rs.registries[name]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Failed to find registry named %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistry returns a registry with a given name.
|
|
||||||
func (rs *inmemRegistryService) GetRegistry(name string) (*common.Registry, error) {
|
|
||||||
r, ok := rs.registries[name]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Failed to find registry named %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete deletes the registry with a given name.
|
|
||||||
func (rs *inmemRegistryService) Delete(name string) error {
|
|
||||||
_, ok := rs.registries[name]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("Failed to find registry named %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(rs.registries, name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByURL returns a registry that handles the types for a given URL.
|
|
||||||
func (rs *inmemRegistryService) GetByURL(URL string) (*common.Registry, error) {
|
|
||||||
trimmed := util.TrimURLScheme(URL)
|
|
||||||
for _, r := range rs.registries {
|
|
||||||
if strings.HasPrefix(trimmed, util.TrimURLScheme(r.URL)) {
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("Failed to find registry for url: %s", URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryByURL returns a registry that handles the types for a given URL.
|
|
||||||
func (rs *inmemRegistryService) GetRegistryByURL(URL string) (*common.Registry, error) {
|
|
||||||
trimmed := util.TrimURLScheme(URL)
|
|
||||||
for _, r := range rs.registries {
|
|
||||||
if strings.HasPrefix(trimmed, util.TrimURLScheme(r.URL)) {
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("Failed to find registry for url: %s", URL)
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
"github.com/kubernetes/helm/pkg/util"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Registry abstracts a registry that holds charts, which can be
|
|
||||||
// used in a Deployment Manager configuration. There can be multiple
|
|
||||||
// registry implementations.
|
|
||||||
type Registry interface {
|
|
||||||
// Also handles http.Client.Do method for authenticated File accesses
|
|
||||||
util.HTTPDoer
|
|
||||||
|
|
||||||
// GetRegistryName returns the name of this registry
|
|
||||||
GetRegistryName() string
|
|
||||||
// GetRegistryType returns the type of this registry.
|
|
||||||
GetRegistryType() common.RegistryType
|
|
||||||
// GetRegistryShortURL returns the short URL for this registry.
|
|
||||||
GetRegistryShortURL() string
|
|
||||||
// GetRegistryFormat returns the format of this registry.
|
|
||||||
GetRegistryFormat() common.RegistryFormat
|
|
||||||
|
|
||||||
// ListTypes lists types in this registry whose string values conform to the
|
|
||||||
// supplied regular expression, or all types, if the regular expression is nil.
|
|
||||||
ListTypes(regex *regexp.Regexp) ([]Type, error)
|
|
||||||
// GetDownloadURLs returns the URLs required to download the type contents.
|
|
||||||
GetDownloadURLs(t Type) ([]*url.URL, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GithubRegistry abstracts a registry that resides in a Github repository.
|
|
||||||
type GithubRegistry interface {
|
|
||||||
Registry // A GithubRegistry is a Registry.
|
|
||||||
// GetRegistryOwner returns the owner name for this registry
|
|
||||||
GetRegistryOwner() string
|
|
||||||
// GetRegistryRepository returns the repository name for this registry.
|
|
||||||
GetRegistryRepository() string
|
|
||||||
// GetRegistryPath returns the path to the registry in the repository.
|
|
||||||
GetRegistryPath() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ObjectStorageRegistry abstracts a registry that resides in an Object Storage, for
|
|
||||||
// example Google Cloud Storage or AWS S3, etc.
|
|
||||||
type ObjectStorageRegistry interface {
|
|
||||||
Registry // An ObjectStorageRegistry is a Registry.
|
|
||||||
GetBucket() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type describes a type stored in a registry.
|
|
||||||
type Type struct {
|
|
||||||
Collection string
|
|
||||||
Name string
|
|
||||||
Version SemVer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewType initializes a type
|
|
||||||
func NewType(collection, name, version string) (Type, error) {
|
|
||||||
result := Type{Collection: collection, Name: name}
|
|
||||||
err := result.SetVersion(version)
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTypeOrDie initializes a type and panics if initialization fails
|
|
||||||
func NewTypeOrDie(collection, name, version string) Type {
|
|
||||||
result, err := NewType(collection, name, version)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type conforms to the Stringer interface.
|
|
||||||
func (t Type) String() string {
|
|
||||||
var result string
|
|
||||||
if t.Collection != "" {
|
|
||||||
result = t.Collection + "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
result = result + t.Name
|
|
||||||
version := t.GetVersion()
|
|
||||||
if version != "" && version != "v0" {
|
|
||||||
result = result + ":" + version
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVersion returns the type version with the letter "v" prepended.
|
|
||||||
func (t Type) GetVersion() string {
|
|
||||||
var result string
|
|
||||||
version := t.Version.String()
|
|
||||||
if version != "0" {
|
|
||||||
result = "v" + version
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetVersion strips the letter "v" from version, if present,
|
|
||||||
// and sets the the version of the type to the result.
|
|
||||||
func (t *Type) SetVersion(version string) error {
|
|
||||||
vstring := strings.TrimPrefix(version, "v")
|
|
||||||
s, err := ParseSemVer(vstring)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Version = s
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseType takes a registry type string and parses it into a *registry.Type.
|
|
||||||
// TODO: needs better validation that this is actually a registry type.
|
|
||||||
func ParseType(ts string) (Type, error) {
|
|
||||||
tt := Type{}
|
|
||||||
tList := strings.Split(ts, ":")
|
|
||||||
if len(tList) == 2 {
|
|
||||||
if err := tt.SetVersion(tList[1]); err != nil {
|
|
||||||
return tt, fmt.Errorf("malformed type string: %s", ts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cList := strings.Split(tList[0], "/")
|
|
||||||
if len(cList) == 1 {
|
|
||||||
tt.Name = tList[0]
|
|
||||||
} else {
|
|
||||||
tt.Collection = cList[0]
|
|
||||||
tt.Name = cList[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return tt, nil
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTypeConversion(t *testing.T) {
|
|
||||||
// TODO: Are there some real-world examples we want to validate here?
|
|
||||||
tests := map[string]Type{
|
|
||||||
"foo": NewTypeOrDie("", "foo", ""),
|
|
||||||
"foo:v1": NewTypeOrDie("", "foo", "v1"),
|
|
||||||
"github.com/foo": NewTypeOrDie("github.com", "foo", ""),
|
|
||||||
"github.com/foo:v1.2.3": NewTypeOrDie("github.com", "foo", "v1.2.3"),
|
|
||||||
}
|
|
||||||
|
|
||||||
for in, expect := range tests {
|
|
||||||
out, err := ParseType(in)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error parsing type string %s: %s", in, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if out.Name != expect.Name {
|
|
||||||
t.Errorf("Expected name to be %q, got %q", expect.Name, out.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if out.GetVersion() != expect.GetVersion() {
|
|
||||||
t.Errorf("Expected version to be %q, got %q", expect.GetVersion(), out.GetVersion())
|
|
||||||
}
|
|
||||||
|
|
||||||
if out.Collection != expect.Collection {
|
|
||||||
t.Errorf("Expected collection to be %q, got %q", expect.Collection, out.Collection)
|
|
||||||
}
|
|
||||||
|
|
||||||
svalue := out.String()
|
|
||||||
if svalue != in {
|
|
||||||
t.Errorf("Expected string value to be %q, got %q", in, svalue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,421 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/google/go-github/github"
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
"github.com/kubernetes/helm/pkg/util"
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
"golang.org/x/oauth2/google"
|
|
||||||
storage "google.golang.org/api/storage/v1"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegistryProvider is a factory for Registry instances.
|
|
||||||
type RegistryProvider interface {
|
|
||||||
GetRegistryByShortURL(URL string) (Registry, error)
|
|
||||||
GetRegistryByName(registryName string) (Registry, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type registryProvider struct {
|
|
||||||
sync.RWMutex
|
|
||||||
rs common.RegistryService
|
|
||||||
grp GithubRegistryProvider
|
|
||||||
gcsrp GCSRegistryProvider
|
|
||||||
cp common.CredentialProvider
|
|
||||||
registries map[string]Registry
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDefaultRegistryProvider creates a default registry provider with the supplied credential.
|
|
||||||
func NewDefaultRegistryProvider(cp common.CredentialProvider, rs common.RegistryService) RegistryProvider {
|
|
||||||
return NewRegistryProvider(rs, NewGithubRegistryProvider(cp), NewGCSRegistryProvider(cp), cp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRegistryProvider creates a new registry provider using the supplied arguments.
|
|
||||||
func NewRegistryProvider(rs common.RegistryService, grp GithubRegistryProvider, gcsrp GCSRegistryProvider, cp common.CredentialProvider) RegistryProvider {
|
|
||||||
if rs == nil {
|
|
||||||
rs = NewInmemRegistryService()
|
|
||||||
}
|
|
||||||
|
|
||||||
if cp == nil {
|
|
||||||
cp = NewInmemCredentialProvider()
|
|
||||||
}
|
|
||||||
|
|
||||||
if grp == nil {
|
|
||||||
grp = NewGithubRegistryProvider(cp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if gcsrp == nil {
|
|
||||||
gcsrp = NewGCSRegistryProvider(cp)
|
|
||||||
}
|
|
||||||
|
|
||||||
registries := make(map[string]Registry)
|
|
||||||
rp := ®istryProvider{rs: rs, grp: grp, gcsrp: gcsrp, cp: cp, registries: registries}
|
|
||||||
return rp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rp *registryProvider) getRegistry(cr common.Registry) (Registry, error) {
|
|
||||||
switch cr.Type {
|
|
||||||
case common.GithubRegistryType:
|
|
||||||
return rp.grp.GetGithubRegistry(cr)
|
|
||||||
case common.GCSRegistryType:
|
|
||||||
log.Printf("Creating a bigstore client using %#v", rp.gcsrp)
|
|
||||||
return rp.gcsrp.GetGCSRegistry(cr)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unknown registry type: %s", cr.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryByShortURL returns the registry identified by a short URL.
|
|
||||||
func (rp *registryProvider) GetRegistryByShortURL(URL string) (Registry, error) {
|
|
||||||
rp.RLock()
|
|
||||||
defer rp.RUnlock()
|
|
||||||
|
|
||||||
result := rp.findRegistryByShortURL(URL)
|
|
||||||
if result == nil {
|
|
||||||
cr, err := rp.rs.GetByURL(URL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := rp.getRegistry(*cr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rp.registries[r.GetRegistryName()] = r
|
|
||||||
result = r
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// findRegistryByShortURL trims the scheme from both the supplied URL
|
|
||||||
// and the short URL returned by GetRegistryShortURL.
|
|
||||||
func (rp *registryProvider) findRegistryByShortURL(URL string) Registry {
|
|
||||||
trimmed := util.TrimURLScheme(URL)
|
|
||||||
for _, r := range rp.registries {
|
|
||||||
if strings.HasPrefix(trimmed, util.TrimURLScheme(r.GetRegistryShortURL())) {
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRegistryByName returns a registry by name.
|
|
||||||
func (rp *registryProvider) GetRegistryByName(registryName string) (Registry, error) {
|
|
||||||
rp.RLock()
|
|
||||||
defer rp.RUnlock()
|
|
||||||
|
|
||||||
cr, err := rp.rs.Get(registryName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := rp.getRegistry(*cr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rp.registries[r.GetRegistryName()] = r
|
|
||||||
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseRegistryFormat creates a map from a registry format string.
|
|
||||||
func ParseRegistryFormat(rf common.RegistryFormat) map[common.RegistryFormat]bool {
|
|
||||||
split := strings.Split(string(rf), ";")
|
|
||||||
var result = map[common.RegistryFormat]bool{}
|
|
||||||
for _, format := range split {
|
|
||||||
result[common.RegistryFormat(format)] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// GithubRegistryProvider is a factory for GithubRegistry instances.
|
|
||||||
type GithubRegistryProvider interface {
|
|
||||||
GetGithubRegistry(cr common.Registry) (GithubRegistry, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type githubRegistryProvider struct {
|
|
||||||
cp common.CredentialProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGithubRegistryProvider creates a GithubRegistryProvider.
|
|
||||||
func NewGithubRegistryProvider(cp common.CredentialProvider) GithubRegistryProvider {
|
|
||||||
if cp == nil {
|
|
||||||
// TODO: replace this panic with an error return.
|
|
||||||
panic(fmt.Errorf("no credential provider"))
|
|
||||||
}
|
|
||||||
return &githubRegistryProvider{cp: cp}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (grp githubRegistryProvider) createGithubClient(credentialName string) (*http.Client, *github.Client, error) {
|
|
||||||
if credentialName == "" {
|
|
||||||
return http.DefaultClient, github.NewClient(nil), nil
|
|
||||||
}
|
|
||||||
c, err := grp.cp.GetCredential(credentialName)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to fetch credential %s: %v", credentialName, err)
|
|
||||||
log.Print("Trying to use unauthenticated client")
|
|
||||||
return http.DefaultClient, github.NewClient(nil), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c != nil {
|
|
||||||
if c.APIToken != "" {
|
|
||||||
ts := oauth2.StaticTokenSource(
|
|
||||||
&oauth2.Token{AccessToken: string(c.APIToken)},
|
|
||||||
)
|
|
||||||
tc := oauth2.NewClient(oauth2.NoContext, ts)
|
|
||||||
return tc, github.NewClient(tc), nil
|
|
||||||
}
|
|
||||||
if c.BasicAuth.Username != "" && c.BasicAuth.Password != "" {
|
|
||||||
tp := github.BasicAuthTransport{
|
|
||||||
Username: c.BasicAuth.Username,
|
|
||||||
Password: c.BasicAuth.Password,
|
|
||||||
}
|
|
||||||
return tp.Client(), github.NewClient(tp.Client()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return nil, nil, fmt.Errorf("No suitable credential found for %s", credentialName)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetGithubRegistry returns a new GithubRegistry. If there's a credential that is specified, will try
|
|
||||||
// to fetch it and use it, and if there's no credential found, will fall back to unauthenticated client.
|
|
||||||
func (grp githubRegistryProvider) GetGithubRegistry(cr common.Registry) (GithubRegistry, error) {
|
|
||||||
// If there's a credential that we need to use, fetch it and create a client for it.
|
|
||||||
httpClient, client, err := grp.createGithubClient(cr.CredentialName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fMap := ParseRegistryFormat(cr.Format)
|
|
||||||
if fMap[common.UnversionedRegistry] && fMap[common.OneLevelRegistry] {
|
|
||||||
return NewGithubPackageRegistry(cr.Name, cr.URL, nil, httpClient, client)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fMap[common.VersionedRegistry] && fMap[common.CollectionRegistry] {
|
|
||||||
return NewGithubTemplateRegistry(cr.Name, cr.URL, nil, httpClient, client)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("unknown registry format: %s", cr.Format)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GCSRegistryProvider is a factory for GCS Registry instances.
|
|
||||||
type GCSRegistryProvider interface {
|
|
||||||
GetGCSRegistry(cr common.Registry) (ObjectStorageRegistry, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type gcsRegistryProvider struct {
|
|
||||||
cp common.CredentialProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGCSRegistryProvider creates a GCSRegistryProvider.
|
|
||||||
func NewGCSRegistryProvider(cp common.CredentialProvider) GCSRegistryProvider {
|
|
||||||
if cp == nil {
|
|
||||||
// TODO: replace this panic with an error return.
|
|
||||||
panic(fmt.Errorf("no credential provider"))
|
|
||||||
}
|
|
||||||
return &gcsRegistryProvider{cp: cp}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetGCSRegistry returns a new Google Cloud Storage . If there's a credential that is specified, will try
|
|
||||||
// to fetch it and use it, and if there's no credential found, will fall back to unauthenticated client.
|
|
||||||
func (gcsrp gcsRegistryProvider) GetGCSRegistry(cr common.Registry) (ObjectStorageRegistry, error) {
|
|
||||||
client, err := gcsrp.createGCSClient(cr.CredentialName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
service, err := storage.New(client)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unable to create storage service: %v", err)
|
|
||||||
}
|
|
||||||
return NewGCSRegistry(cr.Name, cr.URL, client, service)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gcsrp gcsRegistryProvider) createGCSClient(credentialName string) (*http.Client, error) {
|
|
||||||
if credentialName == "" {
|
|
||||||
return http.DefaultClient, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := gcsrp.cp.GetCredential(credentialName)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to fetch credential %s: %v", credentialName, err)
|
|
||||||
log.Print("Trying to use unauthenticated client")
|
|
||||||
return http.DefaultClient, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
config, err := google.JWTConfigFromJSON([]byte(c.ServiceAccount), storage.DevstorageReadOnlyScope)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unable to parse client secret file to config: %v", err)
|
|
||||||
}
|
|
||||||
return config.Client(oauth2.NoContext), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TemplateRegistryMatcher is an RE for a registry type that does support versions and has collections.
|
|
||||||
var TemplateRegistryMatcher = regexp.MustCompile("github.com/(.*)/(.*)/(.*)/(.*):(.*)")
|
|
||||||
|
|
||||||
// PackageRegistryMatcher is an RE for a registry type that does not support versions and does not have collections.
|
|
||||||
var PackageRegistryMatcher = regexp.MustCompile("github.com/(.*)/(.*)/(.*)")
|
|
||||||
|
|
||||||
// GCSRegistryMatcher is an RE for GCS storage
|
|
||||||
var GCSRegistryMatcher = regexp.MustCompile("gs://(.*)/(.*)")
|
|
||||||
|
|
||||||
// IsGithubShortType returns whether a given type is a type description in a short format to a github repository type.
|
|
||||||
// For now, this means using github types:
|
|
||||||
// github.com/owner/repo/qualifier/type:version
|
|
||||||
// for example:
|
|
||||||
// github.com/kubernetes/application-dm-templates/storage/redis:v1
|
|
||||||
func IsGithubShortType(t string) bool {
|
|
||||||
return TemplateRegistryMatcher.MatchString(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsGithubShortPackageType returns whether a given type is a type description in a short format to a github
|
|
||||||
// package repository type.
|
|
||||||
// For now, this means using github types:
|
|
||||||
// github.com/owner/repo/type
|
|
||||||
// for example:
|
|
||||||
// github.com/helm/charts/cassandra
|
|
||||||
func IsGithubShortPackageType(t string) bool {
|
|
||||||
return PackageRegistryMatcher.MatchString(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsGCSShortType returns whether a given type is a type description in a short format to GCS
|
|
||||||
func IsGCSShortType(t string) bool {
|
|
||||||
return strings.HasPrefix(t, "gs://")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDownloadURLs checks a type to see if it is either a short git hub url or a fully specified URL
|
|
||||||
// and returns the URLs that should be used to fetch it. If the url is not fetchable (primitive type
|
|
||||||
// for example), it returns an empty slice.
|
|
||||||
func GetDownloadURLs(rp RegistryProvider, t string) ([]string, Registry, error) {
|
|
||||||
if IsGithubShortType(t) {
|
|
||||||
return ShortTypeToDownloadURLs(rp, t)
|
|
||||||
} else if IsGithubShortPackageType(t) {
|
|
||||||
return ShortTypeToPackageDownloadURLs(rp, t)
|
|
||||||
} else if IsGCSShortType(t) {
|
|
||||||
return ShortTypeToGCSDownloadUrls(rp, t)
|
|
||||||
} else if util.IsHTTPURL(t) {
|
|
||||||
result, err := url.Parse(t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("cannot parse download URL %s: %s", t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return []string{result.String()}, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return []string{}, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShortTypeToDownloadURLs converts a github URL into downloadable URL from github.
|
|
||||||
// Input must be of the type and is assumed to have been validated before this call:
|
|
||||||
// github.com/owner/repo/qualifier/type:version
|
|
||||||
// for example:
|
|
||||||
// github.com/kubernetes/application-dm-templates/storage/redis:v1
|
|
||||||
func ShortTypeToDownloadURLs(rp RegistryProvider, t string) ([]string, Registry, error) {
|
|
||||||
m := TemplateRegistryMatcher.FindStringSubmatch(t)
|
|
||||||
if len(m) != 6 {
|
|
||||||
return nil, nil, fmt.Errorf("cannot parse short github url: %s", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := rp.GetRegistryByShortURL(t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == nil {
|
|
||||||
return nil, nil, fmt.Errorf("cannot get github registry for %s", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
tt, err := NewType(m[3], m[4], m[5])
|
|
||||||
if err != nil {
|
|
||||||
return nil, r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
urls, err := r.GetDownloadURLs(tt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return util.ConvertURLsToStrings(urls), r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShortTypeToPackageDownloadURLs converts a github URL into downloadable URLs from github.
|
|
||||||
// Input must be of the type and is assumed to have been validated before this call:
|
|
||||||
// github.com/owner/repo/type
|
|
||||||
// for example:
|
|
||||||
// github.com/helm/charts/cassandra
|
|
||||||
func ShortTypeToPackageDownloadURLs(rp RegistryProvider, t string) ([]string, Registry, error) {
|
|
||||||
m := PackageRegistryMatcher.FindStringSubmatch(t)
|
|
||||||
if len(m) != 4 {
|
|
||||||
return nil, nil, fmt.Errorf("Failed to parse short github url: %s", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := rp.GetRegistryByShortURL(t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tt, err := NewType("", m[3], "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
urls, err := r.GetDownloadURLs(tt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return util.ConvertURLsToStrings(urls), r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShortTypeToGCSDownloadUrls returns the download URLs for a short type name.
|
|
||||||
func ShortTypeToGCSDownloadUrls(rp RegistryProvider, t string) ([]string, Registry, error) {
|
|
||||||
m := GCSRegistryMatcher.FindStringSubmatch(t)
|
|
||||||
if len(m) != 3 {
|
|
||||||
return nil, nil, fmt.Errorf("Failed to parse short gs url: %s", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := rp.GetRegistryByShortURL(t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
tt, err := NewType(m[1], m[2], "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
urls, err := r.GetDownloadURLs(tt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, r, err
|
|
||||||
}
|
|
||||||
return util.ConvertURLsToStrings(urls), r, err
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testURLConversionDriver(rp RegistryProvider, tests map[string]TestURLAndError, t *testing.T) {
|
|
||||||
for in, expected := range tests {
|
|
||||||
// TODO(vaikas): Test to make sure it's the right registry.
|
|
||||||
actual, _, err := GetDownloadURLs(rp, in)
|
|
||||||
if err != expected.Err {
|
|
||||||
t.Fatalf("failed on: %s : expected error %v but got %v", in, expected.Err, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if actual[0] != expected.URL {
|
|
||||||
t.Fatalf("failed on: %s : expected %s but got %v", in, expected.URL, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShortGithubURLTemplateMapping(t *testing.T) {
|
|
||||||
githubURLMaps := map[Type]TestURLAndError{
|
|
||||||
NewTypeOrDie("common", "replicatedservice", "v1"): {"https://raw.githubusercontent.com/kubernetes/application-dm-templates/master/common/replicatedservice/v1/replicatedservice.py", nil},
|
|
||||||
NewTypeOrDie("storage", "redis", "v1"): {"https://raw.githubusercontent.com/kubernetes/application-dm-templates/master/storage/redis/v1/redis.jinja", nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := map[string]TestURLAndError{
|
|
||||||
"github.com/kubernetes/application-dm-templates/common/replicatedservice:v1": {"https://raw.githubusercontent.com/kubernetes/application-dm-templates/master/common/replicatedservice/v1/replicatedservice.py", nil},
|
|
||||||
"github.com/kubernetes/application-dm-templates/storage/redis:v1": {"https://raw.githubusercontent.com/kubernetes/application-dm-templates/master/storage/redis/v1/redis.jinja", nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
grp := NewTestGithubRegistryProvider("github.com/kubernetes/application-dm-templates", githubURLMaps)
|
|
||||||
// TODO(vaikas): XXXX FIXME Add gcsrp
|
|
||||||
testURLConversionDriver(NewRegistryProvider(nil, grp, nil, NewInmemCredentialProvider()), tests, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShortGithubURLPackageMapping(t *testing.T) {
|
|
||||||
githubURLMaps := map[Type]TestURLAndError{
|
|
||||||
NewTypeOrDie("", "mongodb", ""): {"https://raw.githubusercontent.com/helm/charts/master/mongodb/manifests/mongodb.yaml", nil},
|
|
||||||
NewTypeOrDie("", "redis", ""): {"https://raw.githubusercontent.com/helm/charts/master/redis/manifests/redis.yaml", nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := map[string]TestURLAndError{
|
|
||||||
"github.com/helm/charts/mongodb": {"https://raw.githubusercontent.com/helm/charts/master/mongodb/manifests/mongodb.yaml", nil},
|
|
||||||
"github.com/helm/charts/redis": {"https://raw.githubusercontent.com/helm/charts/master/redis/manifests/redis.yaml", nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
grp := NewTestGithubRegistryProvider("github.com/helm/charts", githubURLMaps)
|
|
||||||
// TODO(vaikas): XXXX FIXME Add gcsrp
|
|
||||||
testURLConversionDriver(NewRegistryProvider(nil, grp, nil, NewInmemCredentialProvider()), tests, t)
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
"github.com/kubernetes/helm/pkg/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
kubePath = flag.String("kubectl", "./kubectl", "The path to the kubectl binary.")
|
|
||||||
kubeService = flag.String("service", "", "The DNS name of the kubernetes service.")
|
|
||||||
kubeServer = flag.String("server", "", "The IP address and optional port of the kubernetes master.")
|
|
||||||
kubeInsecure = flag.Bool("insecure-skip-tls-verify", false, "Do not check the server's certificate for validity.")
|
|
||||||
kubeConfig = flag.String("config", "", "Path to a kubeconfig file.")
|
|
||||||
kubeCertAuth = flag.String("certificate-authority", "", "Path to a file for the certificate authority.")
|
|
||||||
kubeClientCert = flag.String("client-certificate", "", "Path to a client certificate file.")
|
|
||||||
kubeClientKey = flag.String("client-key", "", "Path to a client key file.")
|
|
||||||
kubeToken = flag.String("token", "", "A service account token.")
|
|
||||||
kubeUsername = flag.String("username", "", "The username to use for basic auth.")
|
|
||||||
kubePassword = flag.String("password", "", "The password to use for basic auth.")
|
|
||||||
)
|
|
||||||
|
|
||||||
var kubernetesConfig *util.KubernetesConfig
|
|
||||||
|
|
||||||
const secretType = "Secret"
|
|
||||||
|
|
||||||
// SecretsCredentialProvider provides credentials for registries from Kubernertes secrets.
|
|
||||||
type SecretsCredentialProvider struct {
|
|
||||||
// Actual object that talks to secrets service.
|
|
||||||
k util.Kubernetes
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSecretsCredentialProvider creates a new secrets credential provider.
|
|
||||||
func NewSecretsCredentialProvider() common.CredentialProvider {
|
|
||||||
kubernetesConfig := &util.KubernetesConfig{
|
|
||||||
KubePath: *kubePath,
|
|
||||||
KubeService: *kubeService,
|
|
||||||
KubeServer: *kubeServer,
|
|
||||||
KubeInsecure: *kubeInsecure,
|
|
||||||
KubeConfig: *kubeConfig,
|
|
||||||
KubeCertAuth: *kubeCertAuth,
|
|
||||||
KubeClientCert: *kubeClientCert,
|
|
||||||
KubeClientKey: *kubeClientKey,
|
|
||||||
KubeToken: *kubeToken,
|
|
||||||
KubeUsername: *kubeUsername,
|
|
||||||
KubePassword: *kubePassword,
|
|
||||||
}
|
|
||||||
return &SecretsCredentialProvider{util.NewKubernetesKubectl(kubernetesConfig)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseCredential(credential string) (*common.RegistryCredential, error) {
|
|
||||||
var c util.KubernetesSecret
|
|
||||||
if err := json.Unmarshal([]byte(credential), &c); err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot unmarshal credential '%s' (%#v)", credential, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
d, err := base64.StdEncoding.DecodeString(c.Data["credential"])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot unmarshal credential '%s' (%#v)", c, err)
|
|
||||||
}
|
|
||||||
// And then finally unmarshal it from yaml to common.RegistryCredential
|
|
||||||
r := &common.RegistryCredential{}
|
|
||||||
if err := yaml.Unmarshal(d, &r); err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot unmarshal credential %s (%#v)", c, err)
|
|
||||||
}
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCredential returns a credential by name.
|
|
||||||
func (scp *SecretsCredentialProvider) GetCredential(name string) (*common.RegistryCredential, error) {
|
|
||||||
o, err := scp.k.Get(name, secretType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return parseCredential(o)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCredential sets a credential by name.
|
|
||||||
func (scp *SecretsCredentialProvider) SetCredential(name string, credential *common.RegistryCredential) error {
|
|
||||||
// Marshal the credential & base64 encode it.
|
|
||||||
b, err := yaml.Marshal(credential)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("yaml marshal failed for credential: %s: %v", name, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
enc := base64.StdEncoding.EncodeToString(b)
|
|
||||||
|
|
||||||
// Then create a kubernetes object out of it
|
|
||||||
metadata := make(map[string]string)
|
|
||||||
metadata["name"] = name
|
|
||||||
data := make(map[string]string)
|
|
||||||
data["credential"] = enc
|
|
||||||
obj := &util.KubernetesSecret{
|
|
||||||
Kind: secretType,
|
|
||||||
APIVersion: "v1",
|
|
||||||
Metadata: metadata,
|
|
||||||
Data: data,
|
|
||||||
}
|
|
||||||
ko, err := yaml.Marshal(obj)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("yaml marshal failed for kubernetes object: %s: %v", name, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = scp.k.Create(string(ko))
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SemVer holds a semantic version as defined by semver.io.
|
|
||||||
type SemVer struct {
|
|
||||||
Major uint
|
|
||||||
Minor uint
|
|
||||||
Patch uint
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseSemVer parses a semantic version string.
|
|
||||||
func ParseSemVer(version string) (SemVer, error) {
|
|
||||||
var err error
|
|
||||||
major, minor, patch := uint64(0), uint64(0), uint64(0)
|
|
||||||
if len(version) > 0 {
|
|
||||||
parts := strings.SplitN(version, ".", 3)
|
|
||||||
if len(parts) > 3 {
|
|
||||||
return SemVer{}, fmt.Errorf("invalid semantic version: %s", version)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(parts) < 1 {
|
|
||||||
return SemVer{}, fmt.Errorf("invalid semantic version: %s", version)
|
|
||||||
}
|
|
||||||
|
|
||||||
if parts[0] != "0" {
|
|
||||||
major, err = strconv.ParseUint(parts[0], 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return SemVer{}, fmt.Errorf("invalid semantic version: %s", version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(parts) > 1 {
|
|
||||||
if parts[1] != "0" {
|
|
||||||
minor, err = strconv.ParseUint(parts[1], 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return SemVer{}, fmt.Errorf("invalid semantic version: %s", version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(parts) > 2 {
|
|
||||||
if parts[2] != "0" {
|
|
||||||
patch, err = strconv.ParseUint(parts[2], 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return SemVer{}, fmt.Errorf("invalid semantic version: %s", version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SemVer{Major: uint(major), Minor: uint(minor), Patch: uint(patch)}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsZero returns true if the semantic version is zero.
|
|
||||||
func (s SemVer) IsZero() bool {
|
|
||||||
return s.Major == 0 && s.Minor == 0 && s.Patch == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// SemVer conforms to the Stringer interface.
|
|
||||||
func (s SemVer) String() string {
|
|
||||||
result := strconv.Itoa(int(s.Major))
|
|
||||||
if s.Minor != 0 || s.Patch != 0 {
|
|
||||||
result = result + "." + strconv.Itoa(int(s.Minor))
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Patch != 0 {
|
|
||||||
result = result + "." + strconv.Itoa(int(s.Patch))
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseInvalidVersionFails(t *testing.T) {
|
|
||||||
for _, test := range []string{
|
|
||||||
".",
|
|
||||||
"..",
|
|
||||||
"...",
|
|
||||||
"1.2.3.4",
|
|
||||||
"notAUnit",
|
|
||||||
"1.notAUint",
|
|
||||||
"1.1.notAUint",
|
|
||||||
"-1",
|
|
||||||
"1.-1",
|
|
||||||
"1.1.-1",
|
|
||||||
"1,1",
|
|
||||||
"1.1,1",
|
|
||||||
} {
|
|
||||||
_, err := ParseSemVer(test)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Invalid version parsed successfully: %s\n", test)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseValidVersionSucceeds(t *testing.T) {
|
|
||||||
for _, test := range []struct {
|
|
||||||
String string
|
|
||||||
Version SemVer
|
|
||||||
}{
|
|
||||||
{"", SemVer{0, 0, 0}},
|
|
||||||
{"0", SemVer{0, 0, 0}},
|
|
||||||
{"0.0", SemVer{0, 0, 0}},
|
|
||||||
{"0.0.0", SemVer{0, 0, 0}},
|
|
||||||
{"1", SemVer{1, 0, 0}},
|
|
||||||
{"1.0", SemVer{1, 0, 0}},
|
|
||||||
{"1.0.0", SemVer{1, 0, 0}},
|
|
||||||
{"1.1", SemVer{1, 1, 0}},
|
|
||||||
{"1.1.0", SemVer{1, 1, 0}},
|
|
||||||
{"1.1.1", SemVer{1, 1, 1}},
|
|
||||||
} {
|
|
||||||
result, err := ParseSemVer(test.String)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Valid version %s did not parse successfully\n", test.String)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.Major != test.Version.Major ||
|
|
||||||
result.Minor != test.Version.Minor ||
|
|
||||||
result.Patch != test.Version.Patch {
|
|
||||||
t.Errorf("Valid version %s did not parse correctly: %s\n", test.String, test.Version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConvertSemVerToStringSucceeds(t *testing.T) {
|
|
||||||
for _, test := range []struct {
|
|
||||||
String string
|
|
||||||
Version SemVer
|
|
||||||
}{
|
|
||||||
{"0", SemVer{0, 0, 0}},
|
|
||||||
{"0.1", SemVer{0, 1, 0}},
|
|
||||||
{"0.0.1", SemVer{0, 0, 1}},
|
|
||||||
{"1", SemVer{1, 0, 0}},
|
|
||||||
{"1.1", SemVer{1, 1, 0}},
|
|
||||||
{"1.1.1", SemVer{1, 1, 1}},
|
|
||||||
} {
|
|
||||||
result := test.Version.String()
|
|
||||||
if result != test.String {
|
|
||||||
t.Errorf("Valid version %s did not format correctly: %s\n", test.Version, test.String)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
- name: test1
|
|
||||||
apitoken: token
|
|
||||||
- name: test2
|
|
||||||
basicauth:
|
|
||||||
username: user
|
|
||||||
password: password
|
|
@ -1,152 +0,0 @@
|
|||||||
/*
|
|
||||||
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 registry
|
|
||||||
|
|
||||||
// TODO(jackgr): Mock github repository service to test package and template registry implementations.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/kubernetes/helm/pkg/common"
|
|
||||||
"github.com/kubernetes/helm/pkg/util"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestURLAndError associates a URL with an error string for testing.
|
|
||||||
type TestURLAndError struct {
|
|
||||||
URL string
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// DownloadResponse holds a mock http response for testing.
|
|
||||||
type DownloadResponse struct {
|
|
||||||
Err error
|
|
||||||
Code int
|
|
||||||
Body string
|
|
||||||
}
|
|
||||||
|
|
||||||
type testGithubRegistryProvider struct {
|
|
||||||
shortURL string
|
|
||||||
responses map[Type]TestURLAndError
|
|
||||||
downloadResponses map[string]DownloadResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
type testGithubRegistry struct {
|
|
||||||
githubRegistry
|
|
||||||
responses map[Type]TestURLAndError
|
|
||||||
downloadResponses map[string]DownloadResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTestGithubRegistryProvider creates a test github registry provider.
|
|
||||||
func NewTestGithubRegistryProvider(shortURL string, responses map[Type]TestURLAndError) GithubRegistryProvider {
|
|
||||||
return testGithubRegistryProvider{
|
|
||||||
shortURL: util.TrimURLScheme(shortURL),
|
|
||||||
responses: responses,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTestGithubRegistryProviderWithDownloads creates a test github registry provider with download responses.
|
|
||||||
func NewTestGithubRegistryProviderWithDownloads(shortURL string, responses map[Type]TestURLAndError, downloadResponses map[string]DownloadResponse) GithubRegistryProvider {
|
|
||||||
return testGithubRegistryProvider{
|
|
||||||
shortURL: util.TrimURLScheme(shortURL),
|
|
||||||
responses: responses,
|
|
||||||
downloadResponses: downloadResponses,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetGithubRegistry is a mock implementation of the same method on GithubRegistryProvider.
|
|
||||||
func (tgrp testGithubRegistryProvider) GetGithubRegistry(cr common.Registry) (GithubRegistry, error) {
|
|
||||||
trimmed := util.TrimURLScheme(cr.URL)
|
|
||||||
if strings.HasPrefix(trimmed, tgrp.shortURL) {
|
|
||||||
ghr, err := newGithubRegistry(cr.Name, trimmed, cr.Format, http.DefaultClient, nil)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Errorf("cannot create a github registry: %s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &testGithubRegistry{
|
|
||||||
githubRegistry: *ghr,
|
|
||||||
responses: tgrp.responses,
|
|
||||||
downloadResponses: tgrp.downloadResponses,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
panic(fmt.Errorf("unknown registry: %v", cr))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListTypes is a mock implementation of the same method on GithubRegistryProvider.
|
|
||||||
func (tgr testGithubRegistry) ListTypes(regex *regexp.Regexp) ([]Type, error) {
|
|
||||||
panic(fmt.Errorf("ListTypes should not be called in the test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDownloadURLs is a mock implementation of the same method on GithubRegistryProvider.
|
|
||||||
func (tgr testGithubRegistry) GetDownloadURLs(t Type) ([]*url.URL, error) {
|
|
||||||
result := tgr.responses[t]
|
|
||||||
URL, err := url.Parse(result.URL)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return []*url.URL{URL}, result.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do is a mock implementation of the same method on GithubRegistryProvider.
|
|
||||||
func (tgr testGithubRegistry) Do(req *http.Request) (resp *http.Response, err error) {
|
|
||||||
response := tgr.downloadResponses[req.URL.String()]
|
|
||||||
return &http.Response{StatusCode: response.Code, Body: ioutil.NopCloser(bytes.NewBufferString(response.Body))}, response.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
type testGCSRegistryProvider struct {
|
|
||||||
shortURL string
|
|
||||||
responses map[Type]TestURLAndError
|
|
||||||
}
|
|
||||||
|
|
||||||
type testGCSRegistry struct {
|
|
||||||
GCSRegistry
|
|
||||||
responses map[Type]TestURLAndError
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTestGCSRegistryProvider creates a test GCS registry provider.
|
|
||||||
func NewTestGCSRegistryProvider(shortURL string, responses map[Type]TestURLAndError) GCSRegistryProvider {
|
|
||||||
return testGCSRegistryProvider{
|
|
||||||
shortURL: util.TrimURLScheme(shortURL),
|
|
||||||
responses: responses,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDownloadURLs is a mock implementation of the same method on GCSRegistryProvider.
|
|
||||||
func (tgrp testGCSRegistryProvider) GetGCSRegistry(cr common.Registry) (ObjectStorageRegistry, error) {
|
|
||||||
trimmed := util.TrimURLScheme(cr.URL)
|
|
||||||
if strings.HasPrefix(trimmed, tgrp.shortURL) {
|
|
||||||
gcsr, err := NewGCSRegistry(cr.Name, trimmed, http.DefaultClient, nil)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Errorf("cannot create gcs registry: %s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &testGCSRegistry{
|
|
||||||
GCSRegistry: *gcsr,
|
|
||||||
responses: tgrp.responses,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
panic(fmt.Errorf("unknown registry: %v", cr))
|
|
||||||
}
|
|
Loading…
Reference in new issue