Get addition properties for chart

1, Get readme.md content into addition
2, Get dependency of chart
3, Get values of chart

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
wang yan 2020-02-13 12:26:28 +08:00
parent 5e34ba0c97
commit 8029f70ae5
59 changed files with 2169 additions and 431 deletions

View File

@ -21,7 +21,9 @@ import (
resolv "github.com/goharbor/harbor/src/api/artifact/abstractor/resolver" resolv "github.com/goharbor/harbor/src/api/artifact/abstractor/resolver"
"github.com/goharbor/harbor/src/api/artifact/descriptor" "github.com/goharbor/harbor/src/api/artifact/descriptor"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/chart"
"github.com/goharbor/harbor/src/pkg/repository" "github.com/goharbor/harbor/src/pkg/repository"
v1 "github.com/opencontainers/image-spec/specs-go/v1" v1 "github.com/opencontainers/image-spec/specs-go/v1"
) )
@ -31,7 +33,7 @@ const (
// ArtifactTypeChart defines the artifact type for helm chart // ArtifactTypeChart defines the artifact type for helm chart
ArtifactTypeChart = "CHART" ArtifactTypeChart = "CHART"
AdditionTypeValues = "VALUES.YAML" AdditionTypeValues = "VALUES.YAML"
AdditionTypeReadme = "README" AdditionTypeReadme = "README.MD"
AdditionTypeDependencies = "DEPENDENCIES" AdditionTypeDependencies = "DEPENDENCIES"
// TODO import it from helm chart repository // TODO import it from helm chart repository
mediaType = "application/vnd.cncf.helm.config.v1+json" mediaType = "application/vnd.cncf.helm.config.v1+json"
@ -39,8 +41,9 @@ const (
func init() { func init() {
resolver := &resolver{ resolver := &resolver{
repoMgr: repository.Mgr, repoMgr: repository.Mgr,
blobFetcher: blob.Fcher, blobFetcher: blob.Fcher,
chartOperator: chart.Optr,
} }
if err := resolv.Register(resolver, mediaType); err != nil { if err := resolv.Register(resolver, mediaType); err != nil {
log.Errorf("failed to register resolver for media type %s: %v", mediaType, err) log.Errorf("failed to register resolver for media type %s: %v", mediaType, err)
@ -53,8 +56,9 @@ func init() {
} }
type resolver struct { type resolver struct {
repoMgr repository.Manager repoMgr repository.Manager
blobFetcher blob.Fetcher blobFetcher blob.Fetcher
chartOperator chart.Operator
} }
func (r *resolver) ResolveMetadata(ctx context.Context, manifest []byte, artifact *artifact.Artifact) error { func (r *resolver) ResolveMetadata(ctx context.Context, manifest []byte, artifact *artifact.Artifact) error {
@ -86,7 +90,61 @@ func (r *resolver) ResolveMetadata(ctx context.Context, manifest []byte, artifac
} }
func (r *resolver) ResolveAddition(ctx context.Context, artifact *artifact.Artifact, addition string) (*resolv.Addition, error) { func (r *resolver) ResolveAddition(ctx context.Context, artifact *artifact.Artifact, addition string) (*resolv.Addition, error) {
// TODO implement if addition != AdditionTypeValues && addition != AdditionTypeReadme && addition != AdditionTypeDependencies {
return nil, ierror.New(nil).WithCode(ierror.BadRequestCode).
WithMessage("addition %s isn't supported for %s", addition, ArtifactTypeChart)
}
repository, err := r.repoMgr.Get(ctx, artifact.RepositoryID)
if err != nil {
return nil, err
}
_, content, err := r.blobFetcher.FetchManifest(repository.Name, artifact.Digest)
if err != nil {
return nil, err
}
manifest := &v1.Manifest{}
if err := json.Unmarshal(content, manifest); err != nil {
return nil, err
}
for _, layer := range manifest.Layers {
// chart do have two layers, one is config, we should resolve the other one.
layerDgst := layer.Digest.String()
if layerDgst != manifest.Config.Digest.String() {
content, err = r.blobFetcher.FetchLayer(repository.Name, layerDgst)
if err != nil {
return nil, err
}
chartDetails, err := r.chartOperator.GetDetails(content)
if err != nil {
return nil, err
}
var additionContent []byte
var additionContentType string
switch addition {
case AdditionTypeValues:
additionContent = []byte(chartDetails.Files[AdditionTypeValues])
additionContentType = "text/plain; charset=utf-8"
case AdditionTypeReadme:
additionContent = []byte(chartDetails.Files[AdditionTypeReadme])
additionContentType = "text/markdown; charset=utf-8"
case AdditionTypeDependencies:
additionContent, err = json.Marshal(chartDetails.Dependencies)
if err != nil {
return nil, err
}
additionContentType = "application/json; charset=utf-8"
}
return &resolv.Addition{
Content: additionContent,
ContentType: additionContentType,
}, nil
}
}
return nil, nil return nil, nil
} }

View File

@ -16,10 +16,14 @@ package chart
import ( import (
"github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/models"
ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
chartserver "github.com/goharbor/harbor/src/pkg/chart"
"github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob" "github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob"
"github.com/goharbor/harbor/src/testing/pkg/chart"
"github.com/goharbor/harbor/src/testing/pkg/repository" "github.com/goharbor/harbor/src/testing/pkg/repository"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"k8s.io/helm/pkg/chartutil"
"testing" "testing"
) )
@ -28,14 +32,17 @@ type resolverTestSuite struct {
resolver *resolver resolver *resolver
repoMgr *repository.FakeManager repoMgr *repository.FakeManager
blobFetcher *blob.FakeFetcher blobFetcher *blob.FakeFetcher
chartOptr *chart.FakeOpertaor
} }
func (r *resolverTestSuite) SetupTest() { func (r *resolverTestSuite) SetupTest() {
r.repoMgr = &repository.FakeManager{} r.repoMgr = &repository.FakeManager{}
r.blobFetcher = &blob.FakeFetcher{} r.blobFetcher = &blob.FakeFetcher{}
r.chartOptr = &chart.FakeOpertaor{}
r.resolver = &resolver{ r.resolver = &resolver{
repoMgr: r.repoMgr, repoMgr: r.repoMgr,
blobFetcher: r.blobFetcher, blobFetcher: r.blobFetcher,
chartOperator: r.chartOptr,
} }
} }
@ -95,6 +102,86 @@ func (r *resolverTestSuite) TestResolveMetadata() {
r.Assert().Equal("1.8.2", artifact.ExtraAttrs["appVersion"].(string)) r.Assert().Equal("1.8.2", artifact.ExtraAttrs["appVersion"].(string))
} }
func (r *resolverTestSuite) TestResolveAddition() {
// unknown addition
_, err := r.resolver.ResolveAddition(nil, nil, "unknown_addition")
r.True(ierror.IsErr(err, ierror.BadRequestCode))
chartManifest := `{"schemaVersion":2,"config":{"mediaType":"application/vnd.cncf.helm.config.v1+json","digest":"sha256:76a59ebef39013bf7b57e411629b569a5175590024f31eeaaa577a0f8da9e523","size":528},"layers":[{"mediaType":"application/tar+gzip","digest":"sha256:0bd64cfb958b68c71b46597e22185a41e784dc96e04090bc7d2a480b704c3b65","size":12607}]}`
chartYaml := `{
name:redis,
home:http://redis.io/",
sources:[
https://github.com/bitnami/bitnami-docker-redis"
],
version:3.2.5",
description:Open source, advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets.,
keywords:[
redis,
keyvalue,
database
],
maintainers:[
{
name:bitnami-bot,
email:containers@bitnami.com"
}
],
icon:https://bitnami.com/assets/stacks/redis/img/redis-stack-220x234.png",
apiVersion:v1,
appVersion:4.0.9
}`
chartDetails := &chartserver.VersionDetails{
Dependencies: []*chartutil.Dependency{
{
Name: "harbor",
Version: "v1.10",
Repository: "github.com/goharbor",
},
},
Values: map[string]interface{}{
"cluster.enable": true,
"cluster.slaveCount": 1,
"image.pullPolicy": "Always",
"master.securityContext.runAsUser": 1001,
},
Files: map[string]string{
"README.MD": "This chart bootstraps a [Redis](https://github.com/bitnami/bitnami-docker-redis) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.",
"VALUES.YAML": `image:\n ## Bitnami MongoDB registry\n ##\n registry: docker.io\n ## Bitnami MongoDB image name\n ##\n repository: bitnami/mongodb\n ## Bitnami MongoDB image tag\n ## ref: https://hub.docker.com/r/bitnami/mongodb/tags/\n`,
},
}
artifact := &artifact.Artifact{}
r.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
r.blobFetcher.On("FetchManifest").Return("", []byte(chartManifest), nil)
r.blobFetcher.On("FetchLayer").Return([]byte(chartYaml), nil)
r.chartOptr.On("GetDetails").Return(chartDetails, nil)
// values.yaml
addition, err := r.resolver.ResolveAddition(nil, artifact, AdditionTypeValues)
r.Require().Nil(err)
r.Equal("text/plain; charset=utf-8", addition.ContentType)
r.Equal(`image:\n ## Bitnami MongoDB registry\n ##\n registry: docker.io\n ## Bitnami MongoDB image name\n ##\n repository: bitnami/mongodb\n ## Bitnami MongoDB image tag\n ## ref: https://hub.docker.com/r/bitnami/mongodb/tags/\n`, string(addition.Content))
// README.md
addition, err = r.resolver.ResolveAddition(nil, artifact, AdditionTypeReadme)
r.Require().Nil(err)
r.Equal("text/markdown; charset=utf-8", addition.ContentType)
r.Equal(`This chart bootstraps a [Redis](https://github.com/bitnami/bitnami-docker-redis) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.`, string(addition.Content))
// README.md
addition, err = r.resolver.ResolveAddition(nil, artifact, AdditionTypeDependencies)
r.Require().Nil(err)
r.Equal("application/json; charset=utf-8", addition.ContentType)
r.Equal(`[{"name":"harbor","version":"v1.10","repository":"github.com/goharbor"}]`, string(addition.Content))
}
func (r *resolverTestSuite) TestGetArtifactType() { func (r *resolverTestSuite) TestGetArtifactType() {
r.Assert().Equal(ArtifactTypeChart, r.resolver.GetArtifactType()) r.Assert().Equal(ArtifactTypeChart, r.resolver.GetArtifactType())
} }

View File

@ -24,6 +24,7 @@ require (
github.com/cenkalti/backoff v2.1.1+incompatible // indirect github.com/cenkalti/backoff v2.1.1+incompatible // indirect
github.com/cloudflare/cfssl v0.0.0-20190510060611-9c027c93ba9e // indirect github.com/cloudflare/cfssl v0.0.0-20190510060611-9c027c93ba9e // indirect
github.com/coreos/go-oidc v2.0.0+incompatible github.com/coreos/go-oidc v2.0.0+incompatible
github.com/cyphar/filepath-securejoin v0.2.2 // indirect
github.com/dghubble/sling v1.1.0 github.com/dghubble/sling v1.1.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/docker/distribution v2.7.1+incompatible github.com/docker/distribution v2.7.1+incompatible
@ -84,5 +85,5 @@ require (
k8s.io/api v0.0.0-20190222213804-5cb15d344471 k8s.io/api v0.0.0-20190222213804-5cb15d344471
k8s.io/apimachinery v0.0.0-20180704011316-f534d624797b k8s.io/apimachinery v0.0.0-20180704011316-f534d624797b
k8s.io/client-go v8.0.0+incompatible k8s.io/client-go v8.0.0+incompatible
k8s.io/helm v2.9.1+incompatible k8s.io/helm v2.16.1+incompatible
) )

View File

@ -80,6 +80,8 @@ github.com/couchbase/go-couchbase v0.0.0-20181122212707-3e9b6e1258bb/go.mod h1:T
github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -525,5 +527,5 @@ k8s.io/apimachinery v0.0.0-20180704011316-f534d624797b h1:IEJ1jhyB5TOkHdq5dBEdef
k8s.io/apimachinery v0.0.0-20180704011316-f534d624797b/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= k8s.io/apimachinery v0.0.0-20180704011316-f534d624797b/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/client-go v8.0.0+incompatible h1:tTI4hRmb1DRMl4fG6Vclfdi6nTM82oIrTT7HfitmxC4= k8s.io/client-go v8.0.0+incompatible h1:tTI4hRmb1DRMl4fG6Vclfdi6nTM82oIrTT7HfitmxC4=
k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/helm v2.9.1+incompatible h1:IafoSdCxLzN1yqabsnwwAMSyjuplWVO/jy+MTyHMLIE= k8s.io/helm v2.16.1+incompatible h1:L+k810plJlaGWEw1EszeT4deK8XVaKxac1oGcuB+WDc=
k8s.io/helm v2.9.1+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= k8s.io/helm v2.16.1+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI=

48
src/pkg/chart/model.go Normal file
View File

@ -0,0 +1,48 @@
package chart
import (
"k8s.io/helm/pkg/chartutil"
helm_repo "k8s.io/helm/pkg/repo"
"time"
)
// Version extends the helm Version with additional labels
type Version struct {
helm_repo.ChartVersion
}
// Versions is an array of extended Version
type Versions []*Version
// VersionDetails keeps the detailed data info of the chart version
type VersionDetails struct {
Metadata *helm_repo.ChartVersion `json:"metadata"`
Dependencies []*chartutil.Dependency `json:"dependencies"`
Values map[string]interface{} `json:"values"`
Files map[string]string `json:"files"`
Security *SecurityReport `json:"security"`
}
// SecurityReport keeps the info related with security
// e.g.: digital signature, vulnerability scanning etc.
type SecurityReport struct {
Signature *DigitalSignature `json:"signature"`
}
// DigitalSignature used to indicate if the chart has been signed
type DigitalSignature struct {
Signed bool `json:"signed"`
Provenance string `json:"prov_file"`
}
// Info keeps the information of the chart
type Info struct {
Name string `json:"name"`
TotalVersions uint32 `json:"total_versions"`
LatestVersion string `json:"latest_version"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
Icon string `json:"icon"`
Home string `json:"home"`
Deprecated bool `json:"deprecated"`
}

135
src/pkg/chart/operator.go Normal file
View File

@ -0,0 +1,135 @@
package chart
import (
"bytes"
"errors"
"fmt"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/chart"
)
var (
// Optr is a global chart operator instance
Optr = NewOperator()
)
const (
readmeFileName = "README.MD"
valuesFileName = "VALUES.YAML"
)
// Operator ...
type Operator interface {
// GetChartDetails parse the details from the provided content bytes
GetDetails(content []byte) (*VersionDetails, error)
// FetchLayer the content of layer under the repository
GetData(content []byte) (*chart.Chart, error)
}
var _ Operator = &operator{}
// ChartOperator is designed to process the contents of
// the specified chart version to get more details
type operator struct{}
// NewOperator returns an instance of the default chart opertaor
func NewOperator() Operator {
return &operator{}
}
// GetDetails parse the details from the provided content bytes
func (cho *operator) GetDetails(content []byte) (*VersionDetails, error) {
chartData, err := cho.GetData(content)
if err != nil {
return nil, err
}
// Parse the requirements of chart
requirements, err := chartutil.LoadRequirements(chartData)
if err != nil {
// If no requirements.yaml, return empty dependency list
if _, ok := err.(chartutil.ErrNoRequirementsFile); ok {
requirements = &chartutil.Requirements{
Dependencies: make([]*chartutil.Dependency, 0),
}
} else {
return nil, err
}
}
var values map[string]interface{}
files := make(map[string]string)
// Parse values
if chartData.Values != nil {
values = parseRawValues([]byte(chartData.Values.GetRaw()))
if len(values) > 0 {
// Append values.yaml file
files[valuesFileName] = chartData.Values.Raw
}
}
// Append other files like 'README.md'
for _, v := range chartData.GetFiles() {
if v.TypeUrl == readmeFileName {
files[readmeFileName] = string(v.GetValue())
break
}
}
theChart := &VersionDetails{
Dependencies: requirements.Dependencies,
Values: values,
Files: files,
}
return theChart, nil
}
// GetData returns raw data of chart
func (cho *operator) GetData(content []byte) (*chart.Chart, error) {
if content == nil || len(content) == 0 {
return nil, errors.New("zero content")
}
reader := bytes.NewReader(content)
chartData, err := chartutil.LoadArchive(reader)
if err != nil {
return nil, err
}
return chartData, nil
}
// Parse the raw values to value map
func parseRawValues(rawValue []byte) map[string]interface{} {
valueMap := make(map[string]interface{})
if len(rawValue) == 0 {
return valueMap
}
values, err := chartutil.ReadValues(rawValue)
if err != nil || len(values) == 0 {
return valueMap
}
readValue(values, "", valueMap)
return valueMap
}
// Recursively read value
func readValue(values map[string]interface{}, keyPrefix string, valueMap map[string]interface{}) {
for key, value := range values {
longKey := key
if keyPrefix != "" {
longKey = fmt.Sprintf("%s.%s", keyPrefix, key)
}
if subValues, ok := value.(map[string]interface{}); ok {
readValue(subValues, longKey, valueMap)
} else {
valueMap[longKey] = value
}
}
}

View File

@ -0,0 +1,27 @@
package chart
import (
"testing"
htesting "github.com/goharbor/harbor/src/testing"
)
func TestGetChartDetails(t *testing.T) {
chartOpr := NewOperator()
chartDetails, err := chartOpr.GetDetails(htesting.HelmChartContent)
if err != nil {
t.Fatal(err)
}
if len(chartDetails.Dependencies) == 0 {
t.Fatal("At least 1 dependency exitsing, but we got 0 now")
}
if len(chartDetails.Values) == 0 {
t.Fatal("At least 1 value existing, but we got 0 now")
}
if chartDetails.Values["adminserver.adminPassword"] != "Harbor12345" {
t.Fatalf("The value of 'adminserver.adminPassword' should be 'Harbor12345' but we got '%s' now", chartDetails.Values["adminserver.adminPassword"])
}
}

View File

@ -0,0 +1,32 @@
package chart
import (
chartserver "github.com/goharbor/harbor/src/pkg/chart"
"github.com/stretchr/testify/mock"
"k8s.io/helm/pkg/proto/hapi/chart"
)
// FakeOpertaor ...
type FakeOpertaor struct {
mock.Mock
}
// GetDetails ...
func (f *FakeOpertaor) GetDetails(content []byte) (*chartserver.VersionDetails, error) {
args := f.Called()
var chartDetails *chartserver.VersionDetails
if args.Get(0) != nil {
chartDetails = args.Get(0).(*chartserver.VersionDetails)
}
return chartDetails, args.Error(1)
}
// GetData ...
func (f *FakeOpertaor) GetData(content []byte) (*chart.Chart, error) {
args := f.Called()
var chartData *chart.Chart
if args.Get(0) != nil {
chartData = args.Get(0).(*chart.Chart)
}
return chartData, args.Error(1)
}

View File

@ -0,0 +1,19 @@
# Copyright (C) 2017 SUSE LLC. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
language: go
go:
- 1.7.x
- 1.8.x
- tip
os:
- linux
- osx
script:
- go test -cover -v ./...
notifications:
email: false

View File

@ -0,0 +1,28 @@
Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
Copyright (C) 2017 SUSE LLC. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,65 @@
## `filepath-securejoin` ##
[![Build Status](https://travis-ci.org/cyphar/filepath-securejoin.svg?branch=master)](https://travis-ci.org/cyphar/filepath-securejoin)
An implementation of `SecureJoin`, a [candidate for inclusion in the Go
standard library][go#20126]. The purpose of this function is to be a "secure"
alternative to `filepath.Join`, and in particular it provides certain
guarantees that are not provided by `filepath.Join`.
This is the function prototype:
```go
func SecureJoin(root, unsafePath string) (string, error)
```
This library **guarantees** the following:
* If no error is set, the resulting string **must** be a child path of
`SecureJoin` and will not contain any symlink path components (they will all
be expanded).
* When expanding symlinks, all symlink path components **must** be resolved
relative to the provided root. In particular, this can be considered a
userspace implementation of how `chroot(2)` operates on file paths. Note that
these symlinks will **not** be expanded lexically (`filepath.Clean` is not
called on the input before processing).
* Non-existant path components are unaffected by `SecureJoin` (similar to
`filepath.EvalSymlinks`'s semantics).
* The returned path will always be `filepath.Clean`ed and thus not contain any
`..` components.
A (trivial) implementation of this function on GNU/Linux systems could be done
with the following (note that this requires root privileges and is far more
opaque than the implementation in this library, and also requires that
`readlink` is inside the `root` path):
```go
package securejoin
import (
"os/exec"
"path/filepath"
)
func SecureJoin(root, unsafePath string) (string, error) {
unsafePath = string(filepath.Separator) + unsafePath
cmd := exec.Command("chroot", root,
"readlink", "--canonicalize-missing", "--no-newline", unsafePath)
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
expanded := string(output)
return filepath.Join(root, expanded), nil
}
```
[go#20126]: https://github.com/golang/go/issues/20126
### License ###
The license of this project is the same as Go, which is a BSD 3-clause license
available in the `LICENSE` file.

View File

@ -0,0 +1 @@
0.2.2

View File

@ -0,0 +1,134 @@
// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
// Copyright (C) 2017 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package securejoin is an implementation of the hopefully-soon-to-be-included
// SecureJoin helper that is meant to be part of the "path/filepath" package.
// The purpose of this project is to provide a PoC implementation to make the
// SecureJoin proposal (https://github.com/golang/go/issues/20126) more
// tangible.
package securejoin
import (
"bytes"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/pkg/errors"
)
// ErrSymlinkLoop is returned by SecureJoinVFS when too many symlinks have been
// evaluated in attempting to securely join the two given paths.
var ErrSymlinkLoop = errors.Wrap(syscall.ELOOP, "secure join")
// IsNotExist tells you if err is an error that implies that either the path
// accessed does not exist (or path components don't exist). This is
// effectively a more broad version of os.IsNotExist.
func IsNotExist(err error) bool {
// If it's a bone-fide ENOENT just bail.
if os.IsNotExist(errors.Cause(err)) {
return true
}
// Check that it's not actually an ENOTDIR, which in some cases is a more
// convoluted case of ENOENT (usually involving weird paths).
var errno error
switch err := errors.Cause(err).(type) {
case *os.PathError:
errno = err.Err
case *os.LinkError:
errno = err.Err
case *os.SyscallError:
errno = err.Err
}
return errno == syscall.ENOTDIR || errno == syscall.ENOENT
}
// SecureJoinVFS joins the two given path components (similar to Join) except
// that the returned path is guaranteed to be scoped inside the provided root
// path (when evaluated). Any symbolic links in the path are evaluated with the
// given root treated as the root of the filesystem, similar to a chroot. The
// filesystem state is evaluated through the given VFS interface (if nil, the
// standard os.* family of functions are used).
//
// Note that the guarantees provided by this function only apply if the path
// components in the returned string are not modified (in other words are not
// replaced with symlinks on the filesystem) after this function has returned.
// Such a symlink race is necessarily out-of-scope of SecureJoin.
func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
// Use the os.* VFS implementation if none was specified.
if vfs == nil {
vfs = osVFS{}
}
var path bytes.Buffer
n := 0
for unsafePath != "" {
if n > 255 {
return "", ErrSymlinkLoop
}
// Next path component, p.
i := strings.IndexRune(unsafePath, filepath.Separator)
var p string
if i == -1 {
p, unsafePath = unsafePath, ""
} else {
p, unsafePath = unsafePath[:i], unsafePath[i+1:]
}
// Create a cleaned path, using the lexical semantics of /../a, to
// create a "scoped" path component which can safely be joined to fullP
// for evaluation. At this point, path.String() doesn't contain any
// symlink components.
cleanP := filepath.Clean(string(filepath.Separator) + path.String() + p)
if cleanP == string(filepath.Separator) {
path.Reset()
continue
}
fullP := filepath.Clean(root + cleanP)
// Figure out whether the path is a symlink.
fi, err := vfs.Lstat(fullP)
if err != nil && !IsNotExist(err) {
return "", err
}
// Treat non-existent path components the same as non-symlinks (we
// can't do any better here).
if IsNotExist(err) || fi.Mode()&os.ModeSymlink == 0 {
path.WriteString(p)
path.WriteRune(filepath.Separator)
continue
}
// Only increment when we actually dereference a link.
n++
// It's a symlink, expand it by prepending it to the yet-unparsed path.
dest, err := vfs.Readlink(fullP)
if err != nil {
return "", err
}
// Absolute symlinks reset any work we've already done.
if filepath.IsAbs(dest) {
path.Reset()
}
unsafePath = dest + string(filepath.Separator) + unsafePath
}
// We have to clean path.String() here because it may contain '..'
// components that are entirely lexical, but would be misleading otherwise.
// And finally do a final clean to ensure that root is also lexically
// clean.
fullP := filepath.Clean(string(filepath.Separator) + path.String())
return filepath.Clean(root + fullP), nil
}
// SecureJoin is a wrapper around SecureJoinVFS that just uses the os.* library
// of functions as the VFS. If in doubt, use this function over SecureJoinVFS.
func SecureJoin(root, unsafePath string) (string, error) {
return SecureJoinVFS(root, unsafePath, nil)
}

View File

@ -0,0 +1 @@
github.com/pkg/errors v0.8.0

View File

@ -0,0 +1,41 @@
// Copyright (C) 2017 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package securejoin
import "os"
// In future this should be moved into a separate package, because now there
// are several projects (umoci and go-mtree) that are using this sort of
// interface.
// VFS is the minimal interface necessary to use SecureJoinVFS. A nil VFS is
// equivalent to using the standard os.* family of functions. This is mainly
// used for the purposes of mock testing, but also can be used to otherwise use
// SecureJoin with VFS-like system.
type VFS interface {
// Lstat returns a FileInfo describing the named file. If the file is a
// symbolic link, the returned FileInfo describes the symbolic link. Lstat
// makes no attempt to follow the link. These semantics are identical to
// os.Lstat.
Lstat(name string) (os.FileInfo, error)
// Readlink returns the destination of the named symbolic link. These
// semantics are identical to os.Readlink.
Readlink(name string) (string, error)
}
// osVFS is the "nil" VFS, in that it just passes everything through to the os
// module.
type osVFS struct{}
// Lstat returns a FileInfo describing the named file. If the file is a
// symbolic link, the returned FileInfo describes the symbolic link. Lstat
// makes no attempt to follow the link. These semantics are identical to
// os.Lstat.
func (o osVFS) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) }
// Readlink returns the destination of the named symbolic link. These
// semantics are identical to os.Readlink.
func (o osVFS) Readlink(name string) (string, error) { return os.Readlink(name) }

2
src/vendor/k8s.io/helm/LICENSE generated vendored
View File

@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright 2016 The Kubernetes Authors All Rights Reserved Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -33,6 +33,12 @@ import (
"k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/repo"
) )
const (
sep = "\v"
// verSep is a separator for version fields in map keys.
verSep = "$$"
)
// Result is a search result. // Result is a search result.
// //
// Score indicates how close it is to match. The higher the score, the longer // Score indicates how close it is to match. The higher the score, the longer
@ -49,16 +55,11 @@ type Index struct {
charts map[string]*repo.ChartVersion charts map[string]*repo.ChartVersion
} }
const sep = "\v" // NewIndex creates a new Index.
// NewIndex creats a new Index.
func NewIndex() *Index { func NewIndex() *Index {
return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}} return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}}
} }
// verSep is a separator for version fields in map keys.
const verSep = "$$"
// AddRepo adds a repository index to the search index. // AddRepo adds a repository index to the search index.
func (i *Index) AddRepo(rname string, ind *repo.IndexFile, all bool) { func (i *Index) AddRepo(rname string, ind *repo.IndexFile, all bool) {
ind.SortEntries() ind.SortEntries()

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2017 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@ -12,6 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
//go:generate go run generator/capabilities_default_versions_generate.go
package chartutil package chartutil
@ -24,14 +25,15 @@ import (
) )
var ( var (
// DefaultVersionSet is the default version set, which includes only Core V1 ("v1"). // DefaultVersionSet is the default version set in included in Kubernetes for workloads
DefaultVersionSet = NewVersionSet("v1") // Default versions as of Kubernetes 1.14
DefaultVersionSet = NewVersionSet(defaultVersions()...)
// DefaultKubeVersion is the default kubernetes version // DefaultKubeVersion is the default kubernetes version
DefaultKubeVersion = &version.Info{ DefaultKubeVersion = &version.Info{
Major: "1", Major: "1",
Minor: "9", Minor: "14",
GitVersion: "v1.9.0", GitVersion: "v1.14.0",
GoVersion: runtime.Version(), GoVersion: runtime.Version(),
Compiler: runtime.Compiler, Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
@ -40,9 +42,9 @@ var (
// Capabilities describes the capabilities of the Kubernetes cluster that Tiller is attached to. // Capabilities describes the capabilities of the Kubernetes cluster that Tiller is attached to.
type Capabilities struct { type Capabilities struct {
// List of all supported API versions // APIVersions list of all supported API versions
APIVersions VersionSet APIVersions VersionSet
// KubeVerison is the Kubernetes version // KubeVersion is the Kubernetes version
KubeVersion *version.Info KubeVersion *version.Info
// TillerVersion is the Tiller version // TillerVersion is the Tiller version
// //

View File

@ -0,0 +1,576 @@
/*
Copyright The Helm Authors.
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.
*/
// Code generated by capabilities_default_versions_generate.go; DO NOT EDIT.
package chartutil
func defaultVersions() []string {
return []string{
"__internal",
"__internal/WatchEvent",
"admissionregistration.k8s.io/__internal",
"admissionregistration.k8s.io/__internal/WatchEvent",
"admissionregistration.k8s.io/v1beta1",
"admissionregistration.k8s.io/v1beta1/CreateOptions",
"admissionregistration.k8s.io/v1beta1/DeleteOptions",
"admissionregistration.k8s.io/v1beta1/ExportOptions",
"admissionregistration.k8s.io/v1beta1/GetOptions",
"admissionregistration.k8s.io/v1beta1/ListOptions",
"admissionregistration.k8s.io/v1beta1/MutatingWebhookConfiguration",
"admissionregistration.k8s.io/v1beta1/MutatingWebhookConfigurationList",
"admissionregistration.k8s.io/v1beta1/PatchOptions",
"admissionregistration.k8s.io/v1beta1/UpdateOptions",
"admissionregistration.k8s.io/v1beta1/ValidatingWebhookConfiguration",
"admissionregistration.k8s.io/v1beta1/ValidatingWebhookConfigurationList",
"admissionregistration.k8s.io/v1beta1/WatchEvent",
"apps/__internal",
"apps/__internal/WatchEvent",
"apps/v1",
"apps/v1/ControllerRevision",
"apps/v1/ControllerRevisionList",
"apps/v1/CreateOptions",
"apps/v1/DaemonSet",
"apps/v1/DaemonSetList",
"apps/v1/DeleteOptions",
"apps/v1/Deployment",
"apps/v1/DeploymentList",
"apps/v1/ExportOptions",
"apps/v1/GetOptions",
"apps/v1/ListOptions",
"apps/v1/PatchOptions",
"apps/v1/ReplicaSet",
"apps/v1/ReplicaSetList",
"apps/v1/StatefulSet",
"apps/v1/StatefulSetList",
"apps/v1/UpdateOptions",
"apps/v1/WatchEvent",
"apps/v1beta1",
"apps/v1beta1/ControllerRevision",
"apps/v1beta1/ControllerRevisionList",
"apps/v1beta1/CreateOptions",
"apps/v1beta1/DeleteOptions",
"apps/v1beta1/Deployment",
"apps/v1beta1/DeploymentList",
"apps/v1beta1/DeploymentRollback",
"apps/v1beta1/ExportOptions",
"apps/v1beta1/GetOptions",
"apps/v1beta1/ListOptions",
"apps/v1beta1/PatchOptions",
"apps/v1beta1/Scale",
"apps/v1beta1/StatefulSet",
"apps/v1beta1/StatefulSetList",
"apps/v1beta1/UpdateOptions",
"apps/v1beta1/WatchEvent",
"apps/v1beta2",
"apps/v1beta2/ControllerRevision",
"apps/v1beta2/ControllerRevisionList",
"apps/v1beta2/CreateOptions",
"apps/v1beta2/DaemonSet",
"apps/v1beta2/DaemonSetList",
"apps/v1beta2/DeleteOptions",
"apps/v1beta2/Deployment",
"apps/v1beta2/DeploymentList",
"apps/v1beta2/ExportOptions",
"apps/v1beta2/GetOptions",
"apps/v1beta2/ListOptions",
"apps/v1beta2/PatchOptions",
"apps/v1beta2/ReplicaSet",
"apps/v1beta2/ReplicaSetList",
"apps/v1beta2/Scale",
"apps/v1beta2/StatefulSet",
"apps/v1beta2/StatefulSetList",
"apps/v1beta2/UpdateOptions",
"apps/v1beta2/WatchEvent",
"auditregistration.k8s.io/__internal",
"auditregistration.k8s.io/__internal/WatchEvent",
"auditregistration.k8s.io/v1alpha1",
"auditregistration.k8s.io/v1alpha1/AuditSink",
"auditregistration.k8s.io/v1alpha1/AuditSinkList",
"auditregistration.k8s.io/v1alpha1/CreateOptions",
"auditregistration.k8s.io/v1alpha1/DeleteOptions",
"auditregistration.k8s.io/v1alpha1/ExportOptions",
"auditregistration.k8s.io/v1alpha1/GetOptions",
"auditregistration.k8s.io/v1alpha1/ListOptions",
"auditregistration.k8s.io/v1alpha1/PatchOptions",
"auditregistration.k8s.io/v1alpha1/UpdateOptions",
"auditregistration.k8s.io/v1alpha1/WatchEvent",
"authentication.k8s.io/__internal",
"authentication.k8s.io/__internal/WatchEvent",
"authentication.k8s.io/v1",
"authentication.k8s.io/v1/CreateOptions",
"authentication.k8s.io/v1/DeleteOptions",
"authentication.k8s.io/v1/ExportOptions",
"authentication.k8s.io/v1/GetOptions",
"authentication.k8s.io/v1/ListOptions",
"authentication.k8s.io/v1/PatchOptions",
"authentication.k8s.io/v1/TokenRequest",
"authentication.k8s.io/v1/TokenReview",
"authentication.k8s.io/v1/UpdateOptions",
"authentication.k8s.io/v1/WatchEvent",
"authentication.k8s.io/v1beta1",
"authentication.k8s.io/v1beta1/CreateOptions",
"authentication.k8s.io/v1beta1/DeleteOptions",
"authentication.k8s.io/v1beta1/ExportOptions",
"authentication.k8s.io/v1beta1/GetOptions",
"authentication.k8s.io/v1beta1/ListOptions",
"authentication.k8s.io/v1beta1/PatchOptions",
"authentication.k8s.io/v1beta1/TokenReview",
"authentication.k8s.io/v1beta1/UpdateOptions",
"authentication.k8s.io/v1beta1/WatchEvent",
"authorization.k8s.io/__internal",
"authorization.k8s.io/__internal/WatchEvent",
"authorization.k8s.io/v1",
"authorization.k8s.io/v1/CreateOptions",
"authorization.k8s.io/v1/DeleteOptions",
"authorization.k8s.io/v1/ExportOptions",
"authorization.k8s.io/v1/GetOptions",
"authorization.k8s.io/v1/ListOptions",
"authorization.k8s.io/v1/LocalSubjectAccessReview",
"authorization.k8s.io/v1/PatchOptions",
"authorization.k8s.io/v1/SelfSubjectAccessReview",
"authorization.k8s.io/v1/SelfSubjectRulesReview",
"authorization.k8s.io/v1/SubjectAccessReview",
"authorization.k8s.io/v1/UpdateOptions",
"authorization.k8s.io/v1/WatchEvent",
"authorization.k8s.io/v1beta1",
"authorization.k8s.io/v1beta1/CreateOptions",
"authorization.k8s.io/v1beta1/DeleteOptions",
"authorization.k8s.io/v1beta1/ExportOptions",
"authorization.k8s.io/v1beta1/GetOptions",
"authorization.k8s.io/v1beta1/ListOptions",
"authorization.k8s.io/v1beta1/LocalSubjectAccessReview",
"authorization.k8s.io/v1beta1/PatchOptions",
"authorization.k8s.io/v1beta1/SelfSubjectAccessReview",
"authorization.k8s.io/v1beta1/SelfSubjectRulesReview",
"authorization.k8s.io/v1beta1/SubjectAccessReview",
"authorization.k8s.io/v1beta1/UpdateOptions",
"authorization.k8s.io/v1beta1/WatchEvent",
"autoscaling/__internal",
"autoscaling/__internal/WatchEvent",
"autoscaling/v1",
"autoscaling/v1/CreateOptions",
"autoscaling/v1/DeleteOptions",
"autoscaling/v1/ExportOptions",
"autoscaling/v1/GetOptions",
"autoscaling/v1/HorizontalPodAutoscaler",
"autoscaling/v1/HorizontalPodAutoscalerList",
"autoscaling/v1/ListOptions",
"autoscaling/v1/PatchOptions",
"autoscaling/v1/Scale",
"autoscaling/v1/UpdateOptions",
"autoscaling/v1/WatchEvent",
"autoscaling/v2beta1",
"autoscaling/v2beta1/CreateOptions",
"autoscaling/v2beta1/DeleteOptions",
"autoscaling/v2beta1/ExportOptions",
"autoscaling/v2beta1/GetOptions",
"autoscaling/v2beta1/HorizontalPodAutoscaler",
"autoscaling/v2beta1/HorizontalPodAutoscalerList",
"autoscaling/v2beta1/ListOptions",
"autoscaling/v2beta1/PatchOptions",
"autoscaling/v2beta1/UpdateOptions",
"autoscaling/v2beta1/WatchEvent",
"autoscaling/v2beta2",
"autoscaling/v2beta2/CreateOptions",
"autoscaling/v2beta2/DeleteOptions",
"autoscaling/v2beta2/ExportOptions",
"autoscaling/v2beta2/GetOptions",
"autoscaling/v2beta2/HorizontalPodAutoscaler",
"autoscaling/v2beta2/HorizontalPodAutoscalerList",
"autoscaling/v2beta2/ListOptions",
"autoscaling/v2beta2/PatchOptions",
"autoscaling/v2beta2/UpdateOptions",
"autoscaling/v2beta2/WatchEvent",
"batch/__internal",
"batch/__internal/WatchEvent",
"batch/v1",
"batch/v1/CreateOptions",
"batch/v1/DeleteOptions",
"batch/v1/ExportOptions",
"batch/v1/GetOptions",
"batch/v1/Job",
"batch/v1/JobList",
"batch/v1/ListOptions",
"batch/v1/PatchOptions",
"batch/v1/UpdateOptions",
"batch/v1/WatchEvent",
"batch/v1beta1",
"batch/v1beta1/CreateOptions",
"batch/v1beta1/CronJob",
"batch/v1beta1/CronJobList",
"batch/v1beta1/DeleteOptions",
"batch/v1beta1/ExportOptions",
"batch/v1beta1/GetOptions",
"batch/v1beta1/JobTemplate",
"batch/v1beta1/ListOptions",
"batch/v1beta1/PatchOptions",
"batch/v1beta1/UpdateOptions",
"batch/v1beta1/WatchEvent",
"batch/v2alpha1",
"batch/v2alpha1/CreateOptions",
"batch/v2alpha1/CronJob",
"batch/v2alpha1/CronJobList",
"batch/v2alpha1/DeleteOptions",
"batch/v2alpha1/ExportOptions",
"batch/v2alpha1/GetOptions",
"batch/v2alpha1/JobTemplate",
"batch/v2alpha1/ListOptions",
"batch/v2alpha1/PatchOptions",
"batch/v2alpha1/UpdateOptions",
"batch/v2alpha1/WatchEvent",
"certificates.k8s.io/__internal",
"certificates.k8s.io/__internal/WatchEvent",
"certificates.k8s.io/v1beta1",
"certificates.k8s.io/v1beta1/CertificateSigningRequest",
"certificates.k8s.io/v1beta1/CertificateSigningRequestList",
"certificates.k8s.io/v1beta1/CreateOptions",
"certificates.k8s.io/v1beta1/DeleteOptions",
"certificates.k8s.io/v1beta1/ExportOptions",
"certificates.k8s.io/v1beta1/GetOptions",
"certificates.k8s.io/v1beta1/ListOptions",
"certificates.k8s.io/v1beta1/PatchOptions",
"certificates.k8s.io/v1beta1/UpdateOptions",
"certificates.k8s.io/v1beta1/WatchEvent",
"coordination.k8s.io/__internal",
"coordination.k8s.io/__internal/WatchEvent",
"coordination.k8s.io/v1",
"coordination.k8s.io/v1/CreateOptions",
"coordination.k8s.io/v1/DeleteOptions",
"coordination.k8s.io/v1/ExportOptions",
"coordination.k8s.io/v1/GetOptions",
"coordination.k8s.io/v1/Lease",
"coordination.k8s.io/v1/LeaseList",
"coordination.k8s.io/v1/ListOptions",
"coordination.k8s.io/v1/PatchOptions",
"coordination.k8s.io/v1/UpdateOptions",
"coordination.k8s.io/v1/WatchEvent",
"coordination.k8s.io/v1beta1",
"coordination.k8s.io/v1beta1/CreateOptions",
"coordination.k8s.io/v1beta1/DeleteOptions",
"coordination.k8s.io/v1beta1/ExportOptions",
"coordination.k8s.io/v1beta1/GetOptions",
"coordination.k8s.io/v1beta1/Lease",
"coordination.k8s.io/v1beta1/LeaseList",
"coordination.k8s.io/v1beta1/ListOptions",
"coordination.k8s.io/v1beta1/PatchOptions",
"coordination.k8s.io/v1beta1/UpdateOptions",
"coordination.k8s.io/v1beta1/WatchEvent",
"events.k8s.io/__internal",
"events.k8s.io/__internal/WatchEvent",
"events.k8s.io/v1beta1",
"events.k8s.io/v1beta1/CreateOptions",
"events.k8s.io/v1beta1/DeleteOptions",
"events.k8s.io/v1beta1/Event",
"events.k8s.io/v1beta1/EventList",
"events.k8s.io/v1beta1/ExportOptions",
"events.k8s.io/v1beta1/GetOptions",
"events.k8s.io/v1beta1/ListOptions",
"events.k8s.io/v1beta1/PatchOptions",
"events.k8s.io/v1beta1/UpdateOptions",
"events.k8s.io/v1beta1/WatchEvent",
"extensions/__internal",
"extensions/__internal/WatchEvent",
"extensions/v1beta1",
"extensions/v1beta1/CreateOptions",
"extensions/v1beta1/DaemonSet",
"extensions/v1beta1/DaemonSetList",
"extensions/v1beta1/DeleteOptions",
"extensions/v1beta1/Deployment",
"extensions/v1beta1/DeploymentList",
"extensions/v1beta1/DeploymentRollback",
"extensions/v1beta1/ExportOptions",
"extensions/v1beta1/GetOptions",
"extensions/v1beta1/Ingress",
"extensions/v1beta1/IngressList",
"extensions/v1beta1/ListOptions",
"extensions/v1beta1/NetworkPolicy",
"extensions/v1beta1/NetworkPolicyList",
"extensions/v1beta1/PatchOptions",
"extensions/v1beta1/PodSecurityPolicy",
"extensions/v1beta1/PodSecurityPolicyList",
"extensions/v1beta1/ReplicaSet",
"extensions/v1beta1/ReplicaSetList",
"extensions/v1beta1/ReplicationControllerDummy",
"extensions/v1beta1/Scale",
"extensions/v1beta1/UpdateOptions",
"extensions/v1beta1/WatchEvent",
"networking.k8s.io/__internal",
"networking.k8s.io/__internal/WatchEvent",
"networking.k8s.io/v1",
"networking.k8s.io/v1/CreateOptions",
"networking.k8s.io/v1/DeleteOptions",
"networking.k8s.io/v1/ExportOptions",
"networking.k8s.io/v1/GetOptions",
"networking.k8s.io/v1/ListOptions",
"networking.k8s.io/v1/NetworkPolicy",
"networking.k8s.io/v1/NetworkPolicyList",
"networking.k8s.io/v1/PatchOptions",
"networking.k8s.io/v1/UpdateOptions",
"networking.k8s.io/v1/WatchEvent",
"networking.k8s.io/v1beta1",
"networking.k8s.io/v1beta1/CreateOptions",
"networking.k8s.io/v1beta1/DeleteOptions",
"networking.k8s.io/v1beta1/ExportOptions",
"networking.k8s.io/v1beta1/GetOptions",
"networking.k8s.io/v1beta1/Ingress",
"networking.k8s.io/v1beta1/IngressList",
"networking.k8s.io/v1beta1/ListOptions",
"networking.k8s.io/v1beta1/PatchOptions",
"networking.k8s.io/v1beta1/UpdateOptions",
"networking.k8s.io/v1beta1/WatchEvent",
"node.k8s.io/__internal",
"node.k8s.io/__internal/WatchEvent",
"node.k8s.io/v1alpha1",
"node.k8s.io/v1alpha1/CreateOptions",
"node.k8s.io/v1alpha1/DeleteOptions",
"node.k8s.io/v1alpha1/ExportOptions",
"node.k8s.io/v1alpha1/GetOptions",
"node.k8s.io/v1alpha1/ListOptions",
"node.k8s.io/v1alpha1/PatchOptions",
"node.k8s.io/v1alpha1/RuntimeClass",
"node.k8s.io/v1alpha1/RuntimeClassList",
"node.k8s.io/v1alpha1/UpdateOptions",
"node.k8s.io/v1alpha1/WatchEvent",
"node.k8s.io/v1beta1",
"node.k8s.io/v1beta1/CreateOptions",
"node.k8s.io/v1beta1/DeleteOptions",
"node.k8s.io/v1beta1/ExportOptions",
"node.k8s.io/v1beta1/GetOptions",
"node.k8s.io/v1beta1/ListOptions",
"node.k8s.io/v1beta1/PatchOptions",
"node.k8s.io/v1beta1/RuntimeClass",
"node.k8s.io/v1beta1/RuntimeClassList",
"node.k8s.io/v1beta1/UpdateOptions",
"node.k8s.io/v1beta1/WatchEvent",
"policy/__internal",
"policy/__internal/WatchEvent",
"policy/v1beta1",
"policy/v1beta1/CreateOptions",
"policy/v1beta1/DeleteOptions",
"policy/v1beta1/Eviction",
"policy/v1beta1/ExportOptions",
"policy/v1beta1/GetOptions",
"policy/v1beta1/ListOptions",
"policy/v1beta1/PatchOptions",
"policy/v1beta1/PodDisruptionBudget",
"policy/v1beta1/PodDisruptionBudgetList",
"policy/v1beta1/PodSecurityPolicy",
"policy/v1beta1/PodSecurityPolicyList",
"policy/v1beta1/UpdateOptions",
"policy/v1beta1/WatchEvent",
"rbac.authorization.k8s.io/__internal",
"rbac.authorization.k8s.io/__internal/WatchEvent",
"rbac.authorization.k8s.io/v1",
"rbac.authorization.k8s.io/v1/ClusterRole",
"rbac.authorization.k8s.io/v1/ClusterRoleBinding",
"rbac.authorization.k8s.io/v1/ClusterRoleBindingList",
"rbac.authorization.k8s.io/v1/ClusterRoleList",
"rbac.authorization.k8s.io/v1/CreateOptions",
"rbac.authorization.k8s.io/v1/DeleteOptions",
"rbac.authorization.k8s.io/v1/ExportOptions",
"rbac.authorization.k8s.io/v1/GetOptions",
"rbac.authorization.k8s.io/v1/ListOptions",
"rbac.authorization.k8s.io/v1/PatchOptions",
"rbac.authorization.k8s.io/v1/Role",
"rbac.authorization.k8s.io/v1/RoleBinding",
"rbac.authorization.k8s.io/v1/RoleBindingList",
"rbac.authorization.k8s.io/v1/RoleList",
"rbac.authorization.k8s.io/v1/UpdateOptions",
"rbac.authorization.k8s.io/v1/WatchEvent",
"rbac.authorization.k8s.io/v1alpha1",
"rbac.authorization.k8s.io/v1alpha1/ClusterRole",
"rbac.authorization.k8s.io/v1alpha1/ClusterRoleBinding",
"rbac.authorization.k8s.io/v1alpha1/ClusterRoleBindingList",
"rbac.authorization.k8s.io/v1alpha1/ClusterRoleList",
"rbac.authorization.k8s.io/v1alpha1/CreateOptions",
"rbac.authorization.k8s.io/v1alpha1/DeleteOptions",
"rbac.authorization.k8s.io/v1alpha1/ExportOptions",
"rbac.authorization.k8s.io/v1alpha1/GetOptions",
"rbac.authorization.k8s.io/v1alpha1/ListOptions",
"rbac.authorization.k8s.io/v1alpha1/PatchOptions",
"rbac.authorization.k8s.io/v1alpha1/Role",
"rbac.authorization.k8s.io/v1alpha1/RoleBinding",
"rbac.authorization.k8s.io/v1alpha1/RoleBindingList",
"rbac.authorization.k8s.io/v1alpha1/RoleList",
"rbac.authorization.k8s.io/v1alpha1/UpdateOptions",
"rbac.authorization.k8s.io/v1alpha1/WatchEvent",
"rbac.authorization.k8s.io/v1beta1",
"rbac.authorization.k8s.io/v1beta1/ClusterRole",
"rbac.authorization.k8s.io/v1beta1/ClusterRoleBinding",
"rbac.authorization.k8s.io/v1beta1/ClusterRoleBindingList",
"rbac.authorization.k8s.io/v1beta1/ClusterRoleList",
"rbac.authorization.k8s.io/v1beta1/CreateOptions",
"rbac.authorization.k8s.io/v1beta1/DeleteOptions",
"rbac.authorization.k8s.io/v1beta1/ExportOptions",
"rbac.authorization.k8s.io/v1beta1/GetOptions",
"rbac.authorization.k8s.io/v1beta1/ListOptions",
"rbac.authorization.k8s.io/v1beta1/PatchOptions",
"rbac.authorization.k8s.io/v1beta1/Role",
"rbac.authorization.k8s.io/v1beta1/RoleBinding",
"rbac.authorization.k8s.io/v1beta1/RoleBindingList",
"rbac.authorization.k8s.io/v1beta1/RoleList",
"rbac.authorization.k8s.io/v1beta1/UpdateOptions",
"rbac.authorization.k8s.io/v1beta1/WatchEvent",
"scheduling.k8s.io/__internal",
"scheduling.k8s.io/__internal/WatchEvent",
"scheduling.k8s.io/v1",
"scheduling.k8s.io/v1/CreateOptions",
"scheduling.k8s.io/v1/DeleteOptions",
"scheduling.k8s.io/v1/ExportOptions",
"scheduling.k8s.io/v1/GetOptions",
"scheduling.k8s.io/v1/ListOptions",
"scheduling.k8s.io/v1/PatchOptions",
"scheduling.k8s.io/v1/PriorityClass",
"scheduling.k8s.io/v1/PriorityClassList",
"scheduling.k8s.io/v1/UpdateOptions",
"scheduling.k8s.io/v1/WatchEvent",
"scheduling.k8s.io/v1alpha1",
"scheduling.k8s.io/v1alpha1/CreateOptions",
"scheduling.k8s.io/v1alpha1/DeleteOptions",
"scheduling.k8s.io/v1alpha1/ExportOptions",
"scheduling.k8s.io/v1alpha1/GetOptions",
"scheduling.k8s.io/v1alpha1/ListOptions",
"scheduling.k8s.io/v1alpha1/PatchOptions",
"scheduling.k8s.io/v1alpha1/PriorityClass",
"scheduling.k8s.io/v1alpha1/PriorityClassList",
"scheduling.k8s.io/v1alpha1/UpdateOptions",
"scheduling.k8s.io/v1alpha1/WatchEvent",
"scheduling.k8s.io/v1beta1",
"scheduling.k8s.io/v1beta1/CreateOptions",
"scheduling.k8s.io/v1beta1/DeleteOptions",
"scheduling.k8s.io/v1beta1/ExportOptions",
"scheduling.k8s.io/v1beta1/GetOptions",
"scheduling.k8s.io/v1beta1/ListOptions",
"scheduling.k8s.io/v1beta1/PatchOptions",
"scheduling.k8s.io/v1beta1/PriorityClass",
"scheduling.k8s.io/v1beta1/PriorityClassList",
"scheduling.k8s.io/v1beta1/UpdateOptions",
"scheduling.k8s.io/v1beta1/WatchEvent",
"settings.k8s.io/__internal",
"settings.k8s.io/__internal/WatchEvent",
"settings.k8s.io/v1alpha1",
"settings.k8s.io/v1alpha1/CreateOptions",
"settings.k8s.io/v1alpha1/DeleteOptions",
"settings.k8s.io/v1alpha1/ExportOptions",
"settings.k8s.io/v1alpha1/GetOptions",
"settings.k8s.io/v1alpha1/ListOptions",
"settings.k8s.io/v1alpha1/PatchOptions",
"settings.k8s.io/v1alpha1/PodPreset",
"settings.k8s.io/v1alpha1/PodPresetList",
"settings.k8s.io/v1alpha1/UpdateOptions",
"settings.k8s.io/v1alpha1/WatchEvent",
"storage.k8s.io/__internal",
"storage.k8s.io/__internal/WatchEvent",
"storage.k8s.io/v1",
"storage.k8s.io/v1/CreateOptions",
"storage.k8s.io/v1/DeleteOptions",
"storage.k8s.io/v1/ExportOptions",
"storage.k8s.io/v1/GetOptions",
"storage.k8s.io/v1/ListOptions",
"storage.k8s.io/v1/PatchOptions",
"storage.k8s.io/v1/StorageClass",
"storage.k8s.io/v1/StorageClassList",
"storage.k8s.io/v1/UpdateOptions",
"storage.k8s.io/v1/VolumeAttachment",
"storage.k8s.io/v1/VolumeAttachmentList",
"storage.k8s.io/v1/WatchEvent",
"storage.k8s.io/v1alpha1",
"storage.k8s.io/v1alpha1/CreateOptions",
"storage.k8s.io/v1alpha1/DeleteOptions",
"storage.k8s.io/v1alpha1/ExportOptions",
"storage.k8s.io/v1alpha1/GetOptions",
"storage.k8s.io/v1alpha1/ListOptions",
"storage.k8s.io/v1alpha1/PatchOptions",
"storage.k8s.io/v1alpha1/UpdateOptions",
"storage.k8s.io/v1alpha1/VolumeAttachment",
"storage.k8s.io/v1alpha1/VolumeAttachmentList",
"storage.k8s.io/v1alpha1/WatchEvent",
"storage.k8s.io/v1beta1",
"storage.k8s.io/v1beta1/CSIDriver",
"storage.k8s.io/v1beta1/CSIDriverList",
"storage.k8s.io/v1beta1/CSINode",
"storage.k8s.io/v1beta1/CSINodeList",
"storage.k8s.io/v1beta1/CreateOptions",
"storage.k8s.io/v1beta1/DeleteOptions",
"storage.k8s.io/v1beta1/ExportOptions",
"storage.k8s.io/v1beta1/GetOptions",
"storage.k8s.io/v1beta1/ListOptions",
"storage.k8s.io/v1beta1/PatchOptions",
"storage.k8s.io/v1beta1/StorageClass",
"storage.k8s.io/v1beta1/StorageClassList",
"storage.k8s.io/v1beta1/UpdateOptions",
"storage.k8s.io/v1beta1/VolumeAttachment",
"storage.k8s.io/v1beta1/VolumeAttachmentList",
"storage.k8s.io/v1beta1/WatchEvent",
"v1",
"v1/APIGroup",
"v1/APIGroupList",
"v1/APIResourceList",
"v1/APIVersions",
"v1/Binding",
"v1/ComponentStatus",
"v1/ComponentStatusList",
"v1/ConfigMap",
"v1/ConfigMapList",
"v1/CreateOptions",
"v1/DeleteOptions",
"v1/Endpoints",
"v1/EndpointsList",
"v1/Event",
"v1/EventList",
"v1/ExportOptions",
"v1/GetOptions",
"v1/LimitRange",
"v1/LimitRangeList",
"v1/List",
"v1/ListOptions",
"v1/Namespace",
"v1/NamespaceList",
"v1/Node",
"v1/NodeList",
"v1/NodeProxyOptions",
"v1/PatchOptions",
"v1/PersistentVolume",
"v1/PersistentVolumeClaim",
"v1/PersistentVolumeClaimList",
"v1/PersistentVolumeList",
"v1/Pod",
"v1/PodAttachOptions",
"v1/PodExecOptions",
"v1/PodList",
"v1/PodLogOptions",
"v1/PodPortForwardOptions",
"v1/PodProxyOptions",
"v1/PodStatusResult",
"v1/PodTemplate",
"v1/PodTemplateList",
"v1/RangeAllocation",
"v1/ReplicationController",
"v1/ReplicationControllerList",
"v1/ResourceQuota",
"v1/ResourceQuotaList",
"v1/Secret",
"v1/SecretList",
"v1/SerializedReference",
"v1/Service",
"v1/ServiceAccount",
"v1/ServiceAccountList",
"v1/ServiceList",
"v1/ServiceProxyOptions",
"v1/Status",
"v1/UpdateOptions",
"v1/WatchEvent",
}
}

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -42,10 +42,16 @@ const (
DeploymentName = "deployment.yaml" DeploymentName = "deployment.yaml"
// ServiceName is the name of the example service file. // ServiceName is the name of the example service file.
ServiceName = "service.yaml" ServiceName = "service.yaml"
// ServiceAccountName is the name of the example serviceaccount file.
ServiceAccountName = "serviceaccount.yaml"
// NotesName is the name of the example NOTES.txt file. // NotesName is the name of the example NOTES.txt file.
NotesName = "NOTES.txt" NotesName = "NOTES.txt"
// HelpersName is the name of the example NOTES.txt file. // HelpersName is the name of the example helpers file.
HelpersName = "_helpers.tpl" HelpersName = "_helpers.tpl"
// TemplatesTestsDir is the relative directory name for templates tests.
TemplatesTestsDir = "templates/tests"
// TestConnectionName is the name of the example connection test file.
TestConnectionName = "test-connection.yaml"
) )
const defaultValues = `# Default values for %s. const defaultValues = `# Default values for %s.
@ -59,6 +65,28 @@ image:
tag: stable tag: stable
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name:
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service: service:
type: ClusterIP type: ClusterIP
port: 80 port: 80
@ -68,9 +96,10 @@ ingress:
annotations: {} annotations: {}
# kubernetes.io/ingress.class: nginx # kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true" # kubernetes.io/tls-acme: "true"
path: /
hosts: hosts:
- chart-example.local - host: chart-example.local
paths: []
tls: [] tls: []
# - secretName: chart-example-tls # - secretName: chart-example-tls
# hosts: # hosts:
@ -82,11 +111,11 @@ resources: {}
# resources, such as Minikube. If you do want to specify resources, uncomment the following # resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'. # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits: # limits:
# cpu: 100m # cpu: 100m
# memory: 128Mi # memory: 128Mi
# requests: # requests:
# cpu: 100m # cpu: 100m
# memory: 128Mi # memory: 128Mi
nodeSelector: {} nodeSelector: {}
@ -116,71 +145,81 @@ const defaultIgnore = `# Patterns to ignore when building packages.
.project .project
.idea/ .idea/
*.tmproj *.tmproj
.vscode/
` `
const defaultIngress = `{{- if .Values.ingress.enabled -}} const defaultIngress = `{{- if .Values.ingress.enabled -}}
{{- $fullName := include "<CHARTNAME>.fullname" . -}} {{- $fullName := include "<CHARTNAME>.fullname" . -}}
{{- $ingressPath := .Values.ingress.path -}} {{- $svcPort := .Values.service.port -}}
{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1 apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress kind: Ingress
metadata: metadata:
name: {{ $fullName }} name: {{ $fullName }}
labels: labels:
app: {{ template "<CHARTNAME>.name" . }} {{ include "<CHARTNAME>.labels" . | indent 4 }}
chart: {{ template "<CHARTNAME>.chart" . }} {{- with .Values.ingress.annotations }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
annotations: annotations:
{{ toYaml . | indent 4 }} {{- toYaml . | nindent 4 }}
{{- end }} {{- end }}
spec: spec:
{{- if .Values.ingress.tls }} {{- if .Values.ingress.tls }}
tls: tls:
{{- range .Values.ingress.tls }} {{- range .Values.ingress.tls }}
- hosts: - hosts:
{{- range .hosts }} {{- range .hosts }}
- {{ . }} - {{ . | quote }}
{{- end }} {{- end }}
secretName: {{ .secretName }} secretName: {{ .secretName }}
{{- end }} {{- end }}
{{- end }} {{- end }}
rules: rules:
{{- range .Values.ingress.hosts }} {{- range .Values.ingress.hosts }}
- host: {{ . }} - host: {{ .host | quote }}
http: http:
paths: paths:
- path: {{ $ingressPath }} {{- range .paths }}
- path: {{ . }}
backend: backend:
serviceName: {{ $fullName }} serviceName: {{ $fullName }}
servicePort: http servicePort: {{ $svcPort }}
{{- end }}
{{- end }} {{- end }}
{{- end }} {{- end }}
` `
const defaultDeployment = `apiVersion: apps/v1beta2 const defaultDeployment = `apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: {{ template "<CHARTNAME>.fullname" . }} name: {{ include "<CHARTNAME>.fullname" . }}
labels: labels:
app: {{ template "<CHARTNAME>.name" . }} {{ include "<CHARTNAME>.labels" . | indent 4 }}
chart: {{ template "<CHARTNAME>.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec: spec:
replicas: {{ .Values.replicaCount }} replicas: {{ .Values.replicaCount }}
selector: selector:
matchLabels: matchLabels:
app: {{ template "<CHARTNAME>.name" . }} app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
release: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
template: template:
metadata: metadata:
labels: labels:
app: {{ template "<CHARTNAME>.name" . }} app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
release: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
spec: spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ template "<CHARTNAME>.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers: containers:
- name: {{ .Chart.Name }} - name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: ports:
@ -196,30 +235,27 @@ spec:
path: / path: /
port: http port: http
resources: resources:
{{ toYaml .Values.resources | indent 12 }} {{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }} {{- with .Values.nodeSelector }}
nodeSelector: nodeSelector:
{{ toYaml . | indent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
{{- with .Values.affinity }} {{- with .Values.affinity }}
affinity: affinity:
{{ toYaml . | indent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
{{- with .Values.tolerations }} {{- with .Values.tolerations }}
tolerations: tolerations:
{{ toYaml . | indent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
` `
const defaultService = `apiVersion: v1 const defaultService = `apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: {{ template "<CHARTNAME>.fullname" . }} name: {{ include "<CHARTNAME>.fullname" . }}
labels: labels:
app: {{ template "<CHARTNAME>.name" . }} {{ include "<CHARTNAME>.labels" . | indent 4 }}
chart: {{ template "<CHARTNAME>.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec: spec:
type: {{ .Values.service.type }} type: {{ .Values.service.type }}
ports: ports:
@ -228,26 +264,37 @@ spec:
protocol: TCP protocol: TCP
name: http name: http
selector: selector:
app: {{ template "<CHARTNAME>.name" . }} app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
release: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
`
const defaultServiceAccount = `{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ template "<CHARTNAME>.serviceAccountName" . }}
labels:
{{ include "<CHARTNAME>.labels" . | indent 4 }}
{{- end -}}
` `
const defaultNotes = `1. Get the application URL by running these commands: const defaultNotes = `1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }} {{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }} {{- range $host := .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} {{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }}
{{- end }}
{{- end }} {{- end }}
{{- else if contains "NodePort" .Values.service.type }} {{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "<CHARTNAME>.fullname" . }}) export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "<CHARTNAME>.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }} {{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available. NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "<CHARTNAME>.fullname" . }}' You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "<CHARTNAME>.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "<CHARTNAME>.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "<CHARTNAME>.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }} echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }} {{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "<CHARTNAME>.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "<CHARTNAME>.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application" echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80 kubectl port-forward $POD_NAME 8080:80
{{- end }} {{- end }}
@ -285,6 +332,47 @@ Create chart name and version as used by the chart label.
{{- define "<CHARTNAME>.chart" -}} {{- define "<CHARTNAME>.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}} {{- end -}}
{{/*
Common labels
*/}}
{{- define "<CHARTNAME>.labels" -}}
app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
helm.sh/chart: {{ include "<CHARTNAME>.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{/*
Create the name of the service account to use
*/}}
{{- define "<CHARTNAME>.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "<CHARTNAME>.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}
`
const defaultTestConnection = `apiVersion: v1
kind: Pod
metadata:
name: "{{ include "<CHARTNAME>.fullname" . }}-test-connection"
labels:
{{ include "<CHARTNAME>.labels" . | indent 4 }}
annotations:
"helm.sh/hook": test-success
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "<CHARTNAME>.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
` `
// CreateFrom creates a new chart, but scaffolds it from the src chart. // CreateFrom creates a new chart, but scaffolds it from the src chart.
@ -304,8 +392,9 @@ func CreateFrom(chartfile *chart.Metadata, dest string, src string) error {
} }
schart.Templates = updatedTemplates schart.Templates = updatedTemplates
schart.Values = &chart.Config{Raw: string(Transform(schart.Values.Raw, "<CHARTNAME>", schart.Metadata.Name))} if schart.Values != nil {
schart.Values = &chart.Config{Raw: string(Transform(schart.Values.Raw, "<CHARTNAME>", schart.Metadata.Name))}
}
return SaveDir(schart, dest) return SaveDir(schart, dest)
} }
@ -350,7 +439,7 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) {
} }
} }
for _, d := range []string{TemplatesDir, ChartsDir} { for _, d := range []string{TemplatesDir, TemplatesTestsDir, ChartsDir} {
if err := os.MkdirAll(filepath.Join(cdir, d), 0755); err != nil { if err := os.MkdirAll(filepath.Join(cdir, d), 0755); err != nil {
return cdir, err return cdir, err
} }
@ -385,6 +474,11 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) {
path: filepath.Join(cdir, TemplatesDir, ServiceName), path: filepath.Join(cdir, TemplatesDir, ServiceName),
content: Transform(defaultService, "<CHARTNAME>", chartfile.Name), content: Transform(defaultService, "<CHARTNAME>", chartfile.Name),
}, },
{
// serviceaccount.yaml
path: filepath.Join(cdir, TemplatesDir, ServiceAccountName),
content: Transform(defaultServiceAccount, "<CHARTNAME>", chartfile.Name),
},
{ {
// NOTES.txt // NOTES.txt
path: filepath.Join(cdir, TemplatesDir, NotesName), path: filepath.Join(cdir, TemplatesDir, NotesName),
@ -395,6 +489,11 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) {
path: filepath.Join(cdir, TemplatesDir, HelpersName), path: filepath.Join(cdir, TemplatesDir, HelpersName),
content: Transform(defaultHelpers, "<CHARTNAME>", chartfile.Name), content: Transform(defaultHelpers, "<CHARTNAME>", chartfile.Name),
}, },
{
// test-connection.yaml
path: filepath.Join(cdir, TemplatesTestsDir, TestConnectionName),
content: Transform(defaultTestConnection, "<CHARTNAME>", chartfile.Name),
},
} }
for _, file := range files { for _, file := range files {

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,7 +17,7 @@ limitations under the License.
/*Package chartutil contains tools for working with charts. /*Package chartutil contains tools for working with charts.
Charts are described in the protocol buffer definition (pkg/proto/hapi/charts). Charts are described in the protocol buffer definition (pkg/proto/hapi/charts).
This packe provides utilities for serializing and deserializing charts. This package provides utilities for serializing and deserializing charts.
A chart can be represented on the file system in one of two ways: A chart can be represented on the file system in one of two ways:
@ -25,7 +25,7 @@ A chart can be represented on the file system in one of two ways:
- As a tarred gzipped file containing a directory that then contains a - As a tarred gzipped file containing a directory that then contains a
Chart.yaml file. Chart.yaml file.
This package provides utilitites for working with those file formats. This package provides utilities for working with those file formats.
The preferred way of loading a chart is using 'chartutil.Load`: The preferred way of loading a chart is using 'chartutil.Load`:

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,58 +17,60 @@ limitations under the License.
package chartutil package chartutil
import ( import (
"archive/tar" "errors"
"compress/gzip"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
securejoin "github.com/cyphar/filepath-securejoin"
) )
// Expand uncompresses and extracts a chart into the specified directory. // Expand uncompresses and extracts a chart into the specified directory.
func Expand(dir string, r io.Reader) error { func Expand(dir string, r io.Reader) error {
gr, err := gzip.NewReader(r) files, err := loadArchiveFiles(r)
if err != nil { if err != nil {
return err return err
} }
defer gr.Close()
tr := tar.NewReader(gr)
for {
header, err := tr.Next()
if err == io.EOF {
break
} else if err != nil {
return err
}
//split header name and create missing directories // Get the name of the chart
d, _ := filepath.Split(header.Name) var chartName string
fullDir := filepath.Join(dir, d) for _, file := range files {
_, err = os.Stat(fullDir) if file.Name == "Chart.yaml" {
if err != nil && d != "" { ch, err := UnmarshalChartfile(file.Data)
if err := os.MkdirAll(fullDir, 0700); err != nil { if err != nil {
return err return err
} }
chartName = ch.GetName()
} }
}
if chartName == "" {
return errors.New("chart name not specified")
}
path := filepath.Clean(filepath.Join(dir, header.Name)) // Find the base directory
info := header.FileInfo() chartdir, err := securejoin.SecureJoin(dir, chartName)
if info.IsDir() { if err != nil {
if err = os.MkdirAll(path, info.Mode()); err != nil { return err
return err }
}
continue
}
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode()) // Copy all files verbatim. We don't parse these files because parsing can remove
// comments.
for _, file := range files {
outpath, err := securejoin.SecureJoin(chartdir, file.Name)
if err != nil { if err != nil {
return err return err
} }
_, err = io.Copy(file, tr)
if err != nil { // Make sure the necessary subdirs get created.
file.Close() basedir := filepath.Dir(outpath)
if err := os.MkdirAll(basedir, 0755); err != nil {
return err
}
if err := ioutil.WriteFile(outpath, file.Data, 0644); err != nil {
return err return err
} }
file.Close()
} }
return nil return nil
} }

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@ -36,10 +36,8 @@ type Files map[string][]byte
// Given an []*any.Any (the format for files in a chart.Chart), extract a map of files. // Given an []*any.Any (the format for files in a chart.Chart), extract a map of files.
func NewFiles(from []*any.Any) Files { func NewFiles(from []*any.Any) Files {
files := map[string][]byte{} files := map[string][]byte{}
if from != nil { for _, f := range from {
for _, f := range from { files[f.TypeUrl] = f.Value
files[f.TypeUrl] = f.Value
}
} }
return files return files
} }
@ -211,7 +209,8 @@ func ToToml(v interface{}) string {
// always return a string, even on marshal error (empty string). // always return a string, even on marshal error (empty string).
// //
// This is designed to be called from a template. // This is designed to be called from a template.
func ToJson(v interface{}) string { // TODO: change the function signature in Helm 3
func ToJson(v interface{}) string { // nolint
data, err := json.Marshal(v) data, err := json.Marshal(v)
if err != nil { if err != nil {
// Swallow errors inside of a template. // Swallow errors inside of a template.
@ -226,7 +225,8 @@ func ToJson(v interface{}) string {
// JSON documents. Additionally, because its intended use is within templates // JSON documents. Additionally, because its intended use is within templates
// it tolerates errors. It will insert the returned error message string into // it tolerates errors. It will insert the returned error message string into
// m["Error"] in the returned map. // m["Error"] in the returned map.
func FromJson(str string) map[string]interface{} { // TODO: change the function signature in Helm 3
func FromJson(str string) map[string]interface{} { // nolint
m := map[string]interface{}{} m := map[string]interface{}{}
if err := json.Unmarshal([]byte(str), &m); err != nil { if err := json.Unmarshal([]byte(str), &m); err != nil {

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -24,8 +24,11 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http"
"os" "os"
"path"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"github.com/golang/protobuf/ptypes/any" "github.com/golang/protobuf/ptypes/any"
@ -43,6 +46,7 @@ import (
// If a .helmignore file is present, the directory loader will skip loading any files // If a .helmignore file is present, the directory loader will skip loading any files
// matching it. But .helmignore is not evaluated when reading out of an archive. // matching it. But .helmignore is not evaluated when reading out of an archive.
func Load(name string) (*chart.Chart, error) { func Load(name string) (*chart.Chart, error) {
name = filepath.FromSlash(name)
fi, err := os.Stat(name) fi, err := os.Stat(name)
if err != nil { if err != nil {
return nil, err return nil, err
@ -62,11 +66,13 @@ type BufferedFile struct {
Data []byte Data []byte
} }
// LoadArchive loads from a reader containing a compressed tar archive. var drivePathPattern = regexp.MustCompile(`^[a-zA-Z]:/`)
func LoadArchive(in io.Reader) (*chart.Chart, error) {
// loadArchiveFiles loads files out of an archive
func loadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
unzipped, err := gzip.NewReader(in) unzipped, err := gzip.NewReader(in)
if err != nil { if err != nil {
return &chart.Chart{}, err return nil, err
} }
defer unzipped.Close() defer unzipped.Close()
@ -79,7 +85,7 @@ func LoadArchive(in io.Reader) (*chart.Chart, error) {
break break
} }
if err != nil { if err != nil {
return &chart.Chart{}, err return nil, err
} }
if hd.FileInfo().IsDir() { if hd.FileInfo().IsDir() {
@ -88,6 +94,12 @@ func LoadArchive(in io.Reader) (*chart.Chart, error) {
continue continue
} }
switch hd.Typeflag {
// We don't want to process these extension header files.
case tar.TypeXGlobalHeader, tar.TypeXHeader:
continue
}
// Archive could contain \ if generated on Windows // Archive could contain \ if generated on Windows
delimiter := "/" delimiter := "/"
if strings.ContainsRune(hd.Name, '\\') { if strings.ContainsRune(hd.Name, '\\') {
@ -100,12 +112,33 @@ func LoadArchive(in io.Reader) (*chart.Chart, error) {
// Normalize the path to the / delimiter // Normalize the path to the / delimiter
n = strings.Replace(n, delimiter, "/", -1) n = strings.Replace(n, delimiter, "/", -1)
if path.IsAbs(n) {
return nil, errors.New("chart illegally contains absolute paths")
}
n = path.Clean(n)
if n == "." {
// In this case, the original path was relative when it should have been absolute.
return nil, fmt.Errorf("chart illegally contains content outside the base directory: %q", hd.Name)
}
if strings.HasPrefix(n, "..") {
return nil, errors.New("chart illegally references parent directory")
}
// In some particularly arcane acts of path creativity, it is possible to intermix
// UNIX and Windows style paths in such a way that you produce a result of the form
// c:/foo even after all the built-in absolute path checks. So we explicitly check
// for this condition.
if drivePathPattern.MatchString(n) {
return nil, errors.New("chart contains illegally named files")
}
if parts[0] == "Chart.yaml" { if parts[0] == "Chart.yaml" {
return nil, errors.New("chart yaml not in base directory") return nil, errors.New("chart yaml not in base directory")
} }
if _, err := io.Copy(b, tr); err != nil { if _, err := io.Copy(b, tr); err != nil {
return &chart.Chart{}, err return files, err
} }
files = append(files, &BufferedFile{Name: n, Data: b.Bytes()}) files = append(files, &BufferedFile{Name: n, Data: b.Bytes()})
@ -115,7 +148,15 @@ func LoadArchive(in io.Reader) (*chart.Chart, error) {
if len(files) == 0 { if len(files) == 0 {
return nil, errors.New("no files in chart archive") return nil, errors.New("no files in chart archive")
} }
return files, nil
}
// LoadArchive loads from a reader containing a compressed tar archive.
func LoadArchive(in io.Reader) (*chart.Chart, error) {
files, err := loadArchiveFiles(in)
if err != nil {
return nil, err
}
return LoadFiles(files) return LoadFiles(files)
} }
@ -131,6 +172,10 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
return c, err return c, err
} }
c.Metadata = m c.Metadata = m
var apiVersion = c.Metadata.ApiVersion
if apiVersion != "" && apiVersion != ApiVersionV1 {
return c, fmt.Errorf("apiVersion '%s' is not valid. The value must be \"v1\"", apiVersion)
}
} else if f.Name == "values.toml" { } else if f.Name == "values.toml" {
return c, errors.New("values.toml is illegal as of 2.0.0-alpha.2") return c, errors.New("values.toml is illegal as of 2.0.0-alpha.2")
} else if f.Name == "values.yaml" { } else if f.Name == "values.yaml" {
@ -215,7 +260,45 @@ func LoadFile(name string) (*chart.Chart, error) {
} }
defer raw.Close() defer raw.Close()
return LoadArchive(raw) err = ensureArchive(name, raw)
if err != nil {
return nil, err
}
c, err := LoadArchive(raw)
if err != nil {
if err == gzip.ErrHeader {
return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %s)", name, err)
}
}
return c, err
}
// ensureArchive's job is to return an informative error if the file does not appear to be a gzipped archive.
//
// Sometimes users will provide a values.yaml for an argument where a chart is expected. One common occurrence
// of this is invoking `helm template values.yaml mychart` which would otherwise produce a confusing error
// if we didn't check for this.
func ensureArchive(name string, raw *os.File) error {
defer raw.Seek(0, 0) // reset read offset to allow archive loading to proceed.
// Check the file format to give us a chance to provide the user with more actionable feedback.
buffer := make([]byte, 512)
_, err := raw.Read(buffer)
if err != nil && err != io.EOF {
return fmt.Errorf("file '%s' cannot be read: %s", name, err)
}
if contentType := http.DetectContentType(buffer); contentType != "application/x-gzip" {
// TODO: Is there a way to reliably test if a file content is YAML? ghodss/yaml accepts a wide
// variety of content (Makefile, .zshrc) as valid YAML without errors.
// Wrong content type. Let's check if it's yaml and give an extra hint?
if strings.HasSuffix(name, ".yml") || strings.HasSuffix(name, ".yaml") {
return fmt.Errorf("file '%s' seems to be a YAML file, but I expected a gzipped archive", name)
}
return fmt.Errorf("file '%s' does not appear to be a gzipped archive; got '%s'", name, contentType)
}
return nil
} }
// LoadDir loads from a directory. // LoadDir loads from a directory.
@ -272,6 +355,13 @@ func LoadDir(dir string) (*chart.Chart, error) {
return nil return nil
} }
// Irregular files include devices, sockets, and other uses of files that
// are not regular files. In Go they have a file mode type bit set.
// See https://golang.org/pkg/os/#FileMode for examples.
if !fi.Mode().IsRegular() {
return fmt.Errorf("cannot load irregular file %s as it has file mode type bits set", name)
}
data, err := ioutil.ReadFile(name) data, err := ioutil.ReadFile(name)
if err != nil { if err != nil {
return fmt.Errorf("error reading %s: %s", n, err) return fmt.Errorf("error reading %s: %s", n, err)

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@ -45,7 +45,7 @@ var (
type Dependency struct { type Dependency struct {
// Name is the name of the dependency. // Name is the name of the dependency.
// //
// This must mach the name in the dependency's Chart.yaml. // This must match the name in the dependency's Chart.yaml.
Name string `json:"name"` Name string `json:"name"`
// Version is the version (range) of this chart. // Version is the version (range) of this chart.
// //
@ -85,7 +85,7 @@ type Requirements struct {
// //
// It represents the state that the dependencies should be in. // It represents the state that the dependencies should be in.
type RequirementsLock struct { type RequirementsLock struct {
// Genderated is the date the lock file was last generated. // Generated is the date the lock file was last generated.
Generated time.Time `json:"generated"` Generated time.Time `json:"generated"`
// Digest is a hash of the requirements file used to generate it. // Digest is a hash of the requirements file used to generate it.
Digest string `json:"digest"` Digest string `json:"digest"`
@ -124,7 +124,7 @@ func LoadRequirementsLock(c *chart.Chart) (*RequirementsLock, error) {
} }
// ProcessRequirementsConditions disables charts based on condition path value in values // ProcessRequirementsConditions disables charts based on condition path value in values
func ProcessRequirementsConditions(reqs *Requirements, cvals Values) { func ProcessRequirementsConditions(reqs *Requirements, cvals Values, cpath string) {
var cond string var cond string
var conds []string var conds []string
if reqs == nil || len(reqs.Dependencies) == 0 { if reqs == nil || len(reqs.Dependencies) == 0 {
@ -143,7 +143,7 @@ func ProcessRequirementsConditions(reqs *Requirements, cvals Values) {
for _, c := range conds { for _, c := range conds {
if len(c) > 0 { if len(c) > 0 {
// retrieve value // retrieve value
vv, err := cvals.PathValue(c) vv, err := cvals.PathValue(cpath + c)
if err == nil { if err == nil {
// if not bool, warn // if not bool, warn
if bv, ok := vv.(bool); ok { if bv, ok := vv.(bool); ok {
@ -247,6 +247,10 @@ func getAliasDependency(charts []*chart.Chart, aliasChart *Dependency) *chart.Ch
// ProcessRequirementsEnabled removes disabled charts from dependencies // ProcessRequirementsEnabled removes disabled charts from dependencies
func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error { func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error {
return doProcessRequirementsEnabled(c, v, "")
}
func doProcessRequirementsEnabled(c *chart.Chart, v *chart.Config, path string) error {
reqs, err := LoadRequirements(c) reqs, err := LoadRequirements(c)
if err != nil { if err != nil {
// if not just missing requirements file, return error // if not just missing requirements file, return error
@ -303,7 +307,7 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error {
cc := chart.Config{Raw: yvals} cc := chart.Config{Raw: yvals}
// flag dependencies as enabled/disabled // flag dependencies as enabled/disabled
ProcessRequirementsTags(reqs, cvals) ProcessRequirementsTags(reqs, cvals)
ProcessRequirementsConditions(reqs, cvals) ProcessRequirementsConditions(reqs, cvals, path)
// make a map of charts to remove // make a map of charts to remove
rm := map[string]bool{} rm := map[string]bool{}
for _, r := range reqs.Dependencies { for _, r := range reqs.Dependencies {
@ -323,7 +327,8 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error {
} }
// recursively call self to process sub dependencies // recursively call self to process sub dependencies
for _, t := range cd { for _, t := range cd {
err := ProcessRequirementsEnabled(t, &cc) subpath := path + t.Metadata.Name + "."
err := doProcessRequirementsEnabled(t, &cc, subpath)
// if its not just missing requirements file, return error // if its not just missing requirements file, return error
if nerr, ok := err.(ErrNoRequirementsFile); !ok && err != nil { if nerr, ok := err.(ErrNoRequirementsFile); !ok && err != nil {
return nerr return nerr
@ -391,9 +396,24 @@ func processImportValues(c *chart.Chart) error {
if err != nil { if err != nil {
return err return err
} }
b := make(map[string]interface{}, 0) b := cvals.AsMap()
// import values from each dependency if specified in import-values // import values from each dependency if specified in import-values
for _, r := range reqs.Dependencies { for _, r := range reqs.Dependencies {
// only process raw requirement that is found in chart's dependencies (enabled)
found := false
name := r.Name
for _, v := range c.Dependencies {
if v.Metadata.Name == r.Name {
found = true
}
if v.Metadata.Name == r.Alias {
found = true
name = r.Alias
}
}
if !found {
continue
}
if len(r.ImportValues) > 0 { if len(r.ImportValues) > 0 {
var outiv []interface{} var outiv []interface{}
for _, riv := range r.ImportValues { for _, riv := range r.ImportValues {
@ -404,7 +424,7 @@ func processImportValues(c *chart.Chart) error {
"parent": iv["parent"].(string), "parent": iv["parent"].(string),
} }
outiv = append(outiv, nm) outiv = append(outiv, nm)
s := r.Name + "." + nm["child"] s := name + "." + nm["child"]
// get child table // get child table
vv, err := cvals.Table(s) vv, err := cvals.Table(s)
if err != nil { if err != nil {
@ -413,27 +433,26 @@ func processImportValues(c *chart.Chart) error {
} }
// create value map from child to be merged into parent // create value map from child to be merged into parent
vm := pathToMap(nm["parent"], vv.AsMap()) vm := pathToMap(nm["parent"], vv.AsMap())
b = coalesceTables(cvals, vm) b = coalesceTables(b, vm, c.Metadata.Name)
case string: case string:
nm := map[string]string{ nm := map[string]string{
"child": "exports." + iv, "child": "exports." + iv,
"parent": ".", "parent": ".",
} }
outiv = append(outiv, nm) outiv = append(outiv, nm)
s := r.Name + "." + nm["child"] s := name + "." + nm["child"]
vm, err := cvals.Table(s) vm, err := cvals.Table(s)
if err != nil { if err != nil {
log.Printf("Warning: ImportValues missing table: %v", err) log.Printf("Warning: ImportValues missing table: %v", err)
continue continue
} }
b = coalesceTables(b, vm.AsMap()) b = coalesceTables(b, vm.AsMap(), c.Metadata.Name)
} }
} }
// set our formatted import values // set our formatted import values
r.ImportValues = outiv r.ImportValues = outiv
} }
} }
b = coalesceTables(b, cvals)
y, err := yaml.Marshal(b) y, err := yaml.Marshal(b)
if err != nil { if err != nil {
return err return err

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"time"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
@ -48,12 +49,12 @@ func SaveDir(c *chart.Chart, dest string) error {
// Save values.yaml // Save values.yaml
if c.Values != nil && len(c.Values.Raw) > 0 { if c.Values != nil && len(c.Values.Raw) > 0 {
vf := filepath.Join(outdir, ValuesfileName) vf := filepath.Join(outdir, ValuesfileName)
if err := ioutil.WriteFile(vf, []byte(c.Values.Raw), 0755); err != nil { if err := ioutil.WriteFile(vf, []byte(c.Values.Raw), 0644); err != nil {
return err return err
} }
} }
for _, d := range []string{TemplatesDir, ChartsDir} { for _, d := range []string{TemplatesDir, ChartsDir, TemplatesTestsDir} {
if err := os.MkdirAll(filepath.Join(outdir, d), 0755); err != nil { if err := os.MkdirAll(filepath.Join(outdir, d), 0755); err != nil {
return err return err
} }
@ -62,7 +63,13 @@ func SaveDir(c *chart.Chart, dest string) error {
// Save templates // Save templates
for _, f := range c.Templates { for _, f := range c.Templates {
n := filepath.Join(outdir, f.Name) n := filepath.Join(outdir, f.Name)
if err := ioutil.WriteFile(n, f.Data, 0755); err != nil {
d := filepath.Dir(n)
if err := os.MkdirAll(d, 0755); err != nil {
return err
}
if err := ioutil.WriteFile(n, f.Data, 0644); err != nil {
return err return err
} }
} }
@ -76,7 +83,7 @@ func SaveDir(c *chart.Chart, dest string) error {
return err return err
} }
if err := ioutil.WriteFile(n, f.Value, 0755); err != nil { if err := ioutil.WriteFile(n, f.Value, 0644); err != nil {
return err return err
} }
} }
@ -205,9 +212,10 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
func writeToTar(out *tar.Writer, name string, body []byte) error { func writeToTar(out *tar.Writer, name string, body []byte) error {
// TODO: Do we need to create dummy parent directory names if none exist? // TODO: Do we need to create dummy parent directory names if none exist?
h := &tar.Header{ h := &tar.Header{
Name: name, Name: filepath.ToSlash(name),
Mode: 0755, Mode: 0755,
Size: int64(len(body)), Size: int64(len(body)),
ModTime: time.Now(),
} }
if err := out.WriteHeader(h); err != nil { if err := out.WriteHeader(h); err != nil {
return err return err

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -94,6 +94,22 @@ func (v Values) Encode(w io.Writer) error {
return err return err
} }
// MergeInto takes the properties in src and merges them into Values. Maps
// are merged while values and arrays are replaced.
func (v Values) MergeInto(src Values) {
for key, srcVal := range src {
destVal, found := v[key]
if found && istable(srcVal) && istable(destVal) {
destMap := destVal.(map[string]interface{})
srcMap := srcVal.(map[string]interface{})
Values(destMap).MergeInto(Values(srcMap))
} else {
v[key] = srcVal
}
}
}
func tableLookup(v Values, simple string) (Values, error) { func tableLookup(v Values, simple string) (Values, error) {
v2, ok := v[simple] v2, ok := v[simple]
if !ok { if !ok {
@ -150,15 +166,10 @@ func CoalesceValues(chrt *chart.Chart, vals *chart.Config) (Values, error) {
if err != nil { if err != nil {
return cvals, err return cvals, err
} }
cvals, err = coalesce(chrt, evals) return coalesce(chrt, evals)
if err != nil {
return cvals, err
}
} }
var err error return coalesceDeps(chrt, cvals)
cvals, err = coalesceDeps(chrt, cvals)
return cvals, err
} }
// coalesce coalesces the dest values and the chart values, giving priority to the dest values. // coalesce coalesces the dest values and the chart values, giving priority to the dest values.
@ -170,8 +181,7 @@ func coalesce(ch *chart.Chart, dest map[string]interface{}) (map[string]interfac
if err != nil { if err != nil {
return dest, err return dest, err
} }
coalesceDeps(ch, dest) return coalesceDeps(ch, dest)
return dest, nil
} }
// coalesceDeps coalesces the dependencies of the given chart. // coalesceDeps coalesces the dependencies of the given chart.
@ -187,7 +197,7 @@ func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]in
dvmap := dv.(map[string]interface{}) dvmap := dv.(map[string]interface{})
// Get globals out of dest and merge them into dvmap. // Get globals out of dest and merge them into dvmap.
coalesceGlobals(dvmap, dest) dvmap = coalesceGlobals(dvmap, dest, chrt.Metadata.Name)
var err error var err error
// Now coalesce the rest of the values. // Now coalesce the rest of the values.
@ -203,62 +213,37 @@ func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]in
// coalesceGlobals copies the globals out of src and merges them into dest. // coalesceGlobals copies the globals out of src and merges them into dest.
// //
// For convenience, returns dest. // For convenience, returns dest.
func coalesceGlobals(dest, src map[string]interface{}) map[string]interface{} { func coalesceGlobals(dest, src map[string]interface{}, chartName string) map[string]interface{} {
var dg, sg map[string]interface{} var dg, sg map[string]interface{}
if destglob, ok := dest[GlobalKey]; !ok { if destglob, ok := dest[GlobalKey]; !ok {
dg = map[string]interface{}{} dg = map[string]interface{}{}
} else if dg, ok = destglob.(map[string]interface{}); !ok { } else if dg, ok = destglob.(map[string]interface{}); !ok {
log.Printf("warning: skipping globals because destination %s is not a table.", GlobalKey) log.Printf("Warning: Skipping globals for chart '%s' because destination '%s' is not a table.", chartName, GlobalKey)
return dg return dg
} }
if srcglob, ok := src[GlobalKey]; !ok { if srcglob, ok := src[GlobalKey]; !ok {
sg = map[string]interface{}{} sg = map[string]interface{}{}
} else if sg, ok = srcglob.(map[string]interface{}); !ok { } else if sg, ok = srcglob.(map[string]interface{}); !ok {
log.Printf("warning: skipping globals because source %s is not a table.", GlobalKey) log.Printf("Warning: skipping globals for chart '%s' because source '%s' is not a table.", chartName, GlobalKey)
return dg return dg
} }
rv := make(map[string]interface{})
for k, v := range dest {
rv[k] = v
}
// EXPERIMENTAL: In the past, we have disallowed globals to test tables. This // EXPERIMENTAL: In the past, we have disallowed globals to test tables. This
// reverses that decision. It may somehow be possible to introduce a loop // reverses that decision. It may somehow be possible to introduce a loop
// here, but I haven't found a way. So for the time being, let's allow // here, but I haven't found a way. So for the time being, let's allow
// tables in globals. // tables in globals.
for key, val := range sg {
if istable(val) {
vv := copyMap(val.(map[string]interface{}))
if destv, ok := dg[key]; ok {
if destvmap, ok := destv.(map[string]interface{}); ok {
// Basically, we reverse order of coalesce here to merge
// top-down.
coalesceTables(vv, destvmap)
dg[key] = vv
continue
} else {
log.Printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key)
}
} else {
// Here there is no merge. We're just adding.
dg[key] = vv
}
} else if dv, ok := dg[key]; ok && istable(dv) {
// It's not clear if this condition can actually ever trigger.
log.Printf("key %s is table. Skipping", key)
continue
}
// TODO: Do we need to do any additional checking on the value?
dg[key] = val
}
dest[GlobalKey] = dg
return dest
}
func copyMap(src map[string]interface{}) map[string]interface{} { // Basically, we reverse order of coalesce here to merge
dest := make(map[string]interface{}, len(src)) // top-down.
for k, v := range src { rv[GlobalKey] = coalesceTables(sg, dg, chartName)
dest[k] = v return rv
}
return dest
} }
// coalesceValues builds up a values map for a particular chart. // coalesceValues builds up a values map for a particular chart.
@ -275,60 +260,61 @@ func coalesceValues(c *chart.Chart, v map[string]interface{}) (map[string]interf
// On error, we return just the overridden values. // On error, we return just the overridden values.
// FIXME: We should log this error. It indicates that the YAML data // FIXME: We should log this error. It indicates that the YAML data
// did not parse. // did not parse.
return v, fmt.Errorf("error reading default values (%s): %s", c.Values.Raw, err) return v, fmt.Errorf("Error: Reading chart '%s' default values (%s): %s", c.Metadata.Name, c.Values.Raw, err)
} }
for key, val := range nv { return coalesceTables(v, nv.AsMap(), c.Metadata.Name), nil
if value, ok := v[key]; ok {
if value == nil {
// When the YAML value is null, we remove the value's key.
// This allows Helm's various sources of values (value files or --set) to
// remove incompatible keys from any previous chart, file, or set values.
delete(v, key)
} else if dest, ok := value.(map[string]interface{}); ok {
// if v[key] is a table, merge nv's val table into v[key].
src, ok := val.(map[string]interface{})
if !ok {
log.Printf("warning: skipped value for %s: Not a table.", key)
continue
}
// Because v has higher precedence than nv, dest values override src
// values.
coalesceTables(dest, src)
}
} else {
// If the key is not in v, copy it from nv.
v[key] = val
}
}
return v, nil
} }
// coalesceTables merges a source map into a destination map. // coalesceTables merges a source map into a destination map.
// //
// dest is considered authoritative. // dest is considered authoritative.
func coalesceTables(dst, src map[string]interface{}) map[string]interface{} { func coalesceTables(dst, src map[string]interface{}, chartName string) map[string]interface{} {
// Because dest has higher precedence than src, dest values override src // Because dest has higher precedence than src, dest values override src
// values. // values.
rv := make(map[string]interface{})
for key, val := range src { for key, val := range src {
if istable(val) { dv, ok := dst[key]
if innerdst, ok := dst[key]; !ok { if !ok { // if not in dst, then copy from src
dst[key] = val rv[key] = val
} else if istable(innerdst) {
coalesceTables(innerdst.(map[string]interface{}), val.(map[string]interface{}))
} else {
log.Printf("warning: cannot overwrite table with non table for %s (%v)", key, val)
}
continue
} else if dv, ok := dst[key]; ok && istable(dv) {
log.Printf("warning: destination for %s is a table. Ignoring non-table value %v", key, val)
continue
} else if !ok { // <- ok is still in scope from preceding conditional.
dst[key] = val
continue continue
} }
if dv == nil { // if set to nil in dst, then ignore
// When the YAML value is null, we skip the value's key.
// This allows Helm's various sources of values (value files or --set) to
// remove incompatible keys from any previous chart, file, or set values.
continue
}
srcTable, srcIsTable := val.(map[string]interface{})
dstTable, dstIsTable := dv.(map[string]interface{})
switch {
case srcIsTable && dstIsTable: // both tables, we coalesce
rv[key] = coalesceTables(dstTable, srcTable, chartName)
case srcIsTable && !dstIsTable:
log.Printf("Warning: Merging destination map for chart '%s'. Overwriting table item '%s', with non table value: %v", chartName, key, dv)
rv[key] = dv
case !srcIsTable && dstIsTable:
log.Printf("Warning: Merging destination map for chart '%s'. The destination item '%s' is a table and ignoring the source '%s' as it has a non-table value of: %v", chartName, key, key, val)
rv[key] = dv
default: // neither are tables, simply take the dst value
rv[key] = dv
}
} }
return dst
// do we have anything in dst that wasn't processed already that we need to copy across?
for key, val := range dst {
if val == nil {
continue
}
_, ok := rv[key]
if !ok {
rv[key] = val
}
}
return rv
} }
// ReleaseOptions represents the additional release options needed // ReleaseOptions represents the additional release options needed

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -68,7 +68,7 @@ func (p Providers) ByScheme(scheme string) (Constructor, error) {
} }
// All finds all of the registered getters as a list of Provider instances. // All finds all of the registered getters as a list of Provider instances.
// Currently the build-in http/https getter and the discovered // Currently the built-in http/https getter and the discovered
// plugins with downloader notations are collected. // plugins with downloader notations are collected.
func All(settings environment.EnvSettings) Providers { func All(settings environment.EnvSettings) Providers {
result := Providers{ result := Providers{

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@ -23,11 +23,10 @@ import (
"strings" "strings"
"k8s.io/helm/pkg/tlsutil" "k8s.io/helm/pkg/tlsutil"
"k8s.io/helm/pkg/urlutil"
"k8s.io/helm/pkg/version" "k8s.io/helm/pkg/version"
) )
//HttpGetter is the efault HTTP(/S) backend handler //HttpGetter is the default HTTP(/S) backend handler
// TODO: change the name to HTTPGetter in Helm 3 // TODO: change the name to HTTPGetter in Helm 3
type HttpGetter struct { //nolint type HttpGetter struct { //nolint
client *http.Client client *http.Client
@ -82,27 +81,17 @@ func newHTTPGetter(URL, CertFile, KeyFile, CAFile string) (Getter, error) {
// NewHTTPGetter constructs a valid http/https client as HttpGetter // NewHTTPGetter constructs a valid http/https client as HttpGetter
func NewHTTPGetter(URL, CertFile, KeyFile, CAFile string) (*HttpGetter, error) { func NewHTTPGetter(URL, CertFile, KeyFile, CAFile string) (*HttpGetter, error) {
var client HttpGetter var client HttpGetter
if CertFile != "" && KeyFile != "" { tr := &http.Transport{
tlsConf, err := tlsutil.NewClientTLS(CertFile, KeyFile, CAFile) DisableCompression: true,
if err != nil { Proxy: http.ProxyFromEnvironment,
return &client, fmt.Errorf("can't create TLS config for client: %s", err.Error())
}
tlsConf.BuildNameToCertificate()
sni, err := urlutil.ExtractHostname(URL)
if err != nil {
return &client, err
}
tlsConf.ServerName = sni
client.client = &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConf,
Proxy: http.ProxyFromEnvironment,
},
}
} else {
client.client = http.DefaultClient
} }
if (CertFile != "" && KeyFile != "") || CAFile != "" {
tlsConf, err := tlsutil.NewTLSConfig(URL, CertFile, KeyFile, CAFile)
if err != nil {
return &client, fmt.Errorf("can't create TLS config: %s", err.Error())
}
tr.TLSClientConfig = tlsConf
}
client.client = &http.Client{Transport: tr}
return &client, nil return &client, nil
} }

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@ -21,6 +21,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings"
"k8s.io/helm/pkg/helm/environment" "k8s.io/helm/pkg/helm/environment"
"k8s.io/helm/pkg/plugin" "k8s.io/helm/pkg/plugin"
@ -62,13 +63,15 @@ type pluginGetter struct {
// Get runs downloader plugin command // Get runs downloader plugin command
func (p *pluginGetter) Get(href string) (*bytes.Buffer, error) { func (p *pluginGetter) Get(href string) (*bytes.Buffer, error) {
argv := []string{p.certFile, p.keyFile, p.cAFile, href} commands := strings.Split(p.command, " ")
prog := exec.Command(filepath.Join(p.base, p.command), argv...) argv := append(commands[1:], p.certFile, p.keyFile, p.cAFile, href)
prog := exec.Command(filepath.Join(p.base, commands[0]), argv...)
plugin.SetupPluginEnv(p.settings, p.name, p.base) plugin.SetupPluginEnv(p.settings, p.name, p.base)
prog.Env = os.Environ() prog.Env = os.Environ()
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
prog.Stdout = buf prog.Stdout = buf
prog.Stderr = os.Stderr prog.Stderr = os.Stderr
prog.Stdin = os.Stdin
if err := prog.Run(); err != nil { if err := prog.Run(); err != nil {
if eerr, ok := err.(*exec.ExitError); ok { if eerr, ok := err.(*exec.ExitError); ok {
os.Stderr.Write(eerr.Stderr) os.Stderr.Write(eerr.Stderr)

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -32,6 +32,19 @@ import (
"k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/helm/helmpath"
) )
const (
// DefaultTLSCaCert is the default value for HELM_TLS_CA_CERT
DefaultTLSCaCert = "$HELM_HOME/ca.pem"
// DefaultTLSCert is the default value for HELM_TLS_CERT
DefaultTLSCert = "$HELM_HOME/cert.pem"
// DefaultTLSKeyFile is the default value for HELM_TLS_KEY_FILE
DefaultTLSKeyFile = "$HELM_HOME/key.pem"
// DefaultTLSEnable is the default value for HELM_TLS_ENABLE
DefaultTLSEnable = false
// DefaultTLSVerify is the default value for HELM_TLS_VERIFY
DefaultTLSVerify = false
)
// DefaultHelmHome is the default HELM_HOME. // DefaultHelmHome is the default HELM_HOME.
var DefaultHelmHome = filepath.Join(homedir.HomeDir(), ".helm") var DefaultHelmHome = filepath.Join(homedir.HomeDir(), ".helm")
@ -39,7 +52,7 @@ var DefaultHelmHome = filepath.Join(homedir.HomeDir(), ".helm")
type EnvSettings struct { type EnvSettings struct {
// TillerHost is the host and port of Tiller. // TillerHost is the host and port of Tiller.
TillerHost string TillerHost string
// TillerConnectionTimeout is the duration (in seconds) helm will wait to establish a connection to tiller. // TillerConnectionTimeout is the duration (in seconds) helm will wait to establish a connection to Tiller.
TillerConnectionTimeout int64 TillerConnectionTimeout int64
// TillerNamespace is the namespace in which Tiller runs. // TillerNamespace is the namespace in which Tiller runs.
TillerNamespace string TillerNamespace string
@ -49,16 +62,41 @@ type EnvSettings struct {
Debug bool Debug bool
// KubeContext is the name of the kubeconfig context. // KubeContext is the name of the kubeconfig context.
KubeContext string KubeContext string
// KubeConfig is the path to an explicit kubeconfig file. This overwrites the value in $KUBECONFIG
KubeConfig string
// TLSEnable tells helm to communicate with Tiller via TLS
TLSEnable bool
// TLSVerify tells helm to communicate with Tiller via TLS and to verify remote certificates served by Tiller
TLSVerify bool
// TLSServerName tells helm to verify the hostname on the returned certificates from Tiller
TLSServerName string
// TLSCaCertFile is the path to a TLS CA certificate file
TLSCaCertFile string
// TLSCertFile is the path to a TLS certificate file
TLSCertFile string
// TLSKeyFile is the path to a TLS key file
TLSKeyFile string
} }
// AddFlags binds flags to the given flagset. // AddFlags binds flags to the given flagset.
func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) { func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) {
fs.StringVar((*string)(&s.Home), "home", DefaultHelmHome, "location of your Helm config. Overrides $HELM_HOME") fs.StringVar((*string)(&s.Home), "home", DefaultHelmHome, "Location of your Helm config. Overrides $HELM_HOME")
fs.StringVar(&s.TillerHost, "host", "", "address of Tiller. Overrides $HELM_HOST") fs.StringVar(&s.TillerHost, "host", "", "Address of Tiller. Overrides $HELM_HOST")
fs.StringVar(&s.KubeContext, "kube-context", "", "name of the kubeconfig context to use") fs.StringVar(&s.KubeContext, "kube-context", "", "Name of the kubeconfig context to use")
fs.BoolVar(&s.Debug, "debug", false, "enable verbose output") fs.StringVar(&s.KubeConfig, "kubeconfig", "", "Absolute path of the kubeconfig file to be used")
fs.StringVar(&s.TillerNamespace, "tiller-namespace", "kube-system", "namespace of Tiller") fs.BoolVar(&s.Debug, "debug", false, "Enable verbose output")
fs.Int64Var(&s.TillerConnectionTimeout, "tiller-connection-timeout", int64(300), "the duration (in seconds) Helm will wait to establish a connection to tiller") fs.StringVar(&s.TillerNamespace, "tiller-namespace", "kube-system", "Namespace of Tiller")
fs.Int64Var(&s.TillerConnectionTimeout, "tiller-connection-timeout", int64(300), "The duration (in seconds) Helm will wait to establish a connection to Tiller")
}
// AddFlagsTLS adds the flags for supporting client side TLS to the given flagset.
func (s *EnvSettings) AddFlagsTLS(fs *pflag.FlagSet) {
fs.StringVar(&s.TLSServerName, "tls-hostname", s.TillerHost, "The server name used to verify the hostname on the returned certificates from the server")
fs.StringVar(&s.TLSCaCertFile, "tls-ca-cert", DefaultTLSCaCert, "Path to TLS CA certificate file")
fs.StringVar(&s.TLSCertFile, "tls-cert", DefaultTLSCert, "Path to TLS certificate file")
fs.StringVar(&s.TLSKeyFile, "tls-key", DefaultTLSKeyFile, "Path to TLS key file")
fs.BoolVar(&s.TLSVerify, "tls-verify", DefaultTLSVerify, "Enable TLS for request and verify remote")
fs.BoolVar(&s.TLSEnable, "tls", DefaultTLSEnable, "Enable TLS for request")
} }
// Init sets values from the environment. // Init sets values from the environment.
@ -68,12 +106,11 @@ func (s *EnvSettings) Init(fs *pflag.FlagSet) {
} }
} }
// PluginDirs is the path to the plugin directories. // InitTLS sets TLS values from the environment.
func (s EnvSettings) PluginDirs() string { func (s *EnvSettings) InitTLS(fs *pflag.FlagSet) {
if d, ok := os.LookupEnv("HELM_PLUGIN"); ok { for name, envar := range tlsEnvMap {
return d setFlagFromEnv(name, envar, fs)
} }
return s.Home.Plugins()
} }
// envMap maps flag names to envvars // envMap maps flag names to envvars
@ -84,6 +121,34 @@ var envMap = map[string]string{
"tiller-namespace": "TILLER_NAMESPACE", "tiller-namespace": "TILLER_NAMESPACE",
} }
var tlsEnvMap = map[string]string{
"tls-hostname": "HELM_TLS_HOSTNAME",
"tls-ca-cert": "HELM_TLS_CA_CERT",
"tls-cert": "HELM_TLS_CERT",
"tls-key": "HELM_TLS_KEY",
"tls-verify": "HELM_TLS_VERIFY",
"tls": "HELM_TLS_ENABLE",
}
// PluginDirs is the path to the plugin directories.
func (s EnvSettings) PluginDirs() string {
if d, ok := os.LookupEnv("HELM_PLUGIN"); ok {
return d
}
return s.Home.Plugins()
}
// HelmKeyPassphrase is the passphrase used to sign a helm chart.
func (s EnvSettings) HelmKeyPassphrase() string {
if d, ok := os.LookupEnv("HELM_KEY_PASSPHRASE"); ok {
return d
}
return ""
}
// setFlagFromEnv looks up and sets a flag if the corresponding environment variable changed.
// if the flag with the corresponding name was set during fs.Parse(), then the environment
// variable is ignored.
func setFlagFromEnv(name, envar string, fs *pflag.FlagSet) { func setFlagFromEnv(name, envar string, fs *pflag.FlagSet) {
if fs.Changed(name) { if fs.Changed(name) {
return return

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -77,7 +77,7 @@ func (r *Rules) Len() int {
return len(r.patterns) return len(r.patterns)
} }
// Ignore evalutes the file at the given path, and returns true if it should be ignored. // Ignore evaluates the file at the given path, and returns true if it should be ignored.
// //
// Ignore evaluates path against the rules in order. Evaluation stops when a match // Ignore evaluates path against the rules in order. Evaluation stops when a match
// is found. Matching a negative rule will stop evaluation. // is found. Matching a negative rule will stop evaluation.

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at

View File

@ -1,29 +1,12 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// source: hapi/chart/chart.proto // source: hapi/chart/chart.proto
/*
Package chart is a generated protocol buffer package.
It is generated from these files:
hapi/chart/chart.proto
hapi/chart/config.proto
hapi/chart/metadata.proto
hapi/chart/template.proto
It has these top-level messages:
Chart
Config
Value
Maintainer
Metadata
Template
*/
package chart package chart
import proto "github.com/golang/protobuf/proto" import proto "github.com/golang/protobuf/proto"
import fmt "fmt" import fmt "fmt"
import math "math" import math "math"
import google_protobuf "github.com/golang/protobuf/ptypes/any" import any "github.com/golang/protobuf/ptypes/any"
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal
@ -40,22 +23,44 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// optionally parameterizable templates, and zero or more charts (dependencies). // optionally parameterizable templates, and zero or more charts (dependencies).
type Chart struct { type Chart struct {
// Contents of the Chartfile. // Contents of the Chartfile.
Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata" json:"metadata,omitempty"` Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"`
// Templates for this chart. // Templates for this chart.
Templates []*Template `protobuf:"bytes,2,rep,name=templates" json:"templates,omitempty"` Templates []*Template `protobuf:"bytes,2,rep,name=templates,proto3" json:"templates,omitempty"`
// Charts that this chart depends on. // Charts that this chart depends on.
Dependencies []*Chart `protobuf:"bytes,3,rep,name=dependencies" json:"dependencies,omitempty"` Dependencies []*Chart `protobuf:"bytes,3,rep,name=dependencies,proto3" json:"dependencies,omitempty"`
// Default config for this template. // Default config for this template.
Values *Config `protobuf:"bytes,4,opt,name=values" json:"values,omitempty"` Values *Config `protobuf:"bytes,4,opt,name=values,proto3" json:"values,omitempty"`
// Miscellaneous files in a chart archive, // Miscellaneous files in a chart archive,
// e.g. README, LICENSE, etc. // e.g. README, LICENSE, etc.
Files []*google_protobuf.Any `protobuf:"bytes,5,rep,name=files" json:"files,omitempty"` Files []*any.Any `protobuf:"bytes,5,rep,name=files,proto3" json:"files,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *Chart) Reset() { *m = Chart{} } func (m *Chart) Reset() { *m = Chart{} }
func (m *Chart) String() string { return proto.CompactTextString(m) } func (m *Chart) String() string { return proto.CompactTextString(m) }
func (*Chart) ProtoMessage() {} func (*Chart) ProtoMessage() {}
func (*Chart) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (*Chart) Descriptor() ([]byte, []int) {
return fileDescriptor_chart_3948302f7486cf3d, []int{0}
}
func (m *Chart) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Chart.Unmarshal(m, b)
}
func (m *Chart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Chart.Marshal(b, m, deterministic)
}
func (dst *Chart) XXX_Merge(src proto.Message) {
xxx_messageInfo_Chart.Merge(dst, src)
}
func (m *Chart) XXX_Size() int {
return xxx_messageInfo_Chart.Size(m)
}
func (m *Chart) XXX_DiscardUnknown() {
xxx_messageInfo_Chart.DiscardUnknown(m)
}
var xxx_messageInfo_Chart proto.InternalMessageInfo
func (m *Chart) GetMetadata() *Metadata { func (m *Chart) GetMetadata() *Metadata {
if m != nil { if m != nil {
@ -85,7 +90,7 @@ func (m *Chart) GetValues() *Config {
return nil return nil
} }
func (m *Chart) GetFiles() []*google_protobuf.Any { func (m *Chart) GetFiles() []*any.Any {
if m != nil { if m != nil {
return m.Files return m.Files
} }
@ -96,9 +101,9 @@ func init() {
proto.RegisterType((*Chart)(nil), "hapi.chart.Chart") proto.RegisterType((*Chart)(nil), "hapi.chart.Chart")
} }
func init() { proto.RegisterFile("hapi/chart/chart.proto", fileDescriptor0) } func init() { proto.RegisterFile("hapi/chart/chart.proto", fileDescriptor_chart_3948302f7486cf3d) }
var fileDescriptor0 = []byte{ var fileDescriptor_chart_3948302f7486cf3d = []byte{
// 242 bytes of a gzipped FileDescriptorProto // 242 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0xb1, 0x4e, 0xc3, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0xb1, 0x4e, 0xc3, 0x30,
0x10, 0x86, 0x15, 0x4a, 0x0a, 0x1c, 0x2c, 0x58, 0x08, 0x4c, 0xa7, 0x8a, 0x09, 0x75, 0x70, 0x50, 0x10, 0x86, 0x15, 0x4a, 0x0a, 0x1c, 0x2c, 0x58, 0x08, 0x4c, 0xa7, 0x8a, 0x09, 0x75, 0x70, 0x50,

View File

@ -12,16 +12,44 @@ var _ = proto.Marshal
var _ = fmt.Errorf var _ = fmt.Errorf
var _ = math.Inf var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// Config supplies values to the parametrizable templates of a chart. // Config supplies values to the parametrizable templates of a chart.
type Config struct { type Config struct {
Raw string `protobuf:"bytes,1,opt,name=raw" json:"raw,omitempty"` Raw string `protobuf:"bytes,1,opt,name=raw,proto3" json:"raw,omitempty"`
Values map[string]*Value `protobuf:"bytes,2,rep,name=values" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Values map[string]*Value `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *Config) Reset() { *m = Config{} } func (m *Config) Reset() { *m = Config{} }
func (m *Config) String() string { return proto.CompactTextString(m) } func (m *Config) String() string { return proto.CompactTextString(m) }
func (*Config) ProtoMessage() {} func (*Config) ProtoMessage() {}
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} } func (*Config) Descriptor() ([]byte, []int) {
return fileDescriptor_config_7b4c7a75f66363c1, []int{0}
}
func (m *Config) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Config.Unmarshal(m, b)
}
func (m *Config) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Config.Marshal(b, m, deterministic)
}
func (dst *Config) XXX_Merge(src proto.Message) {
xxx_messageInfo_Config.Merge(dst, src)
}
func (m *Config) XXX_Size() int {
return xxx_messageInfo_Config.Size(m)
}
func (m *Config) XXX_DiscardUnknown() {
xxx_messageInfo_Config.DiscardUnknown(m)
}
var xxx_messageInfo_Config proto.InternalMessageInfo
func (m *Config) GetRaw() string { func (m *Config) GetRaw() string {
if m != nil { if m != nil {
@ -39,13 +67,35 @@ func (m *Config) GetValues() map[string]*Value {
// Value describes a configuration value as a string. // Value describes a configuration value as a string.
type Value struct { type Value struct {
Value string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"` Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *Value) Reset() { *m = Value{} } func (m *Value) Reset() { *m = Value{} }
func (m *Value) String() string { return proto.CompactTextString(m) } func (m *Value) String() string { return proto.CompactTextString(m) }
func (*Value) ProtoMessage() {} func (*Value) ProtoMessage() {}
func (*Value) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} } func (*Value) Descriptor() ([]byte, []int) {
return fileDescriptor_config_7b4c7a75f66363c1, []int{1}
}
func (m *Value) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Value.Unmarshal(m, b)
}
func (m *Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Value.Marshal(b, m, deterministic)
}
func (dst *Value) XXX_Merge(src proto.Message) {
xxx_messageInfo_Value.Merge(dst, src)
}
func (m *Value) XXX_Size() int {
return xxx_messageInfo_Value.Size(m)
}
func (m *Value) XXX_DiscardUnknown() {
xxx_messageInfo_Value.DiscardUnknown(m)
}
var xxx_messageInfo_Value proto.InternalMessageInfo
func (m *Value) GetValue() string { func (m *Value) GetValue() string {
if m != nil { if m != nil {
@ -56,12 +106,13 @@ func (m *Value) GetValue() string {
func init() { func init() {
proto.RegisterType((*Config)(nil), "hapi.chart.Config") proto.RegisterType((*Config)(nil), "hapi.chart.Config")
proto.RegisterMapType((map[string]*Value)(nil), "hapi.chart.Config.ValuesEntry")
proto.RegisterType((*Value)(nil), "hapi.chart.Value") proto.RegisterType((*Value)(nil), "hapi.chart.Value")
} }
func init() { proto.RegisterFile("hapi/chart/config.proto", fileDescriptor1) } func init() { proto.RegisterFile("hapi/chart/config.proto", fileDescriptor_config_7b4c7a75f66363c1) }
var fileDescriptor1 = []byte{ var fileDescriptor_config_7b4c7a75f66363c1 = []byte{
// 182 bytes of a gzipped FileDescriptorProto // 182 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xcf, 0x48, 0x2c, 0xc8, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xcf, 0x48, 0x2c, 0xc8,
0xd4, 0x4f, 0xce, 0x48, 0x2c, 0x2a, 0xd1, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xd4, 0x4f, 0xce, 0x48, 0x2c, 0x2a, 0xd1, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28,

View File

@ -12,6 +12,12 @@ var _ = proto.Marshal
var _ = fmt.Errorf var _ = fmt.Errorf
var _ = math.Inf var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Metadata_Engine int32 type Metadata_Engine int32
const ( const (
@ -31,22 +37,46 @@ var Metadata_Engine_value = map[string]int32{
func (x Metadata_Engine) String() string { func (x Metadata_Engine) String() string {
return proto.EnumName(Metadata_Engine_name, int32(x)) return proto.EnumName(Metadata_Engine_name, int32(x))
} }
func (Metadata_Engine) EnumDescriptor() ([]byte, []int) { return fileDescriptor2, []int{1, 0} } func (Metadata_Engine) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_metadata_bafee76586953fd5, []int{1, 0}
}
// Maintainer describes a Chart maintainer. // Maintainer describes a Chart maintainer.
type Maintainer struct { type Maintainer struct {
// Name is a user name or organization name // Name is a user name or organization name
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Email is an optional email address to contact the named maintainer // Email is an optional email address to contact the named maintainer
Email string `protobuf:"bytes,2,opt,name=email" json:"email,omitempty"` Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
// Url is an optional URL to an address for the named maintainer // Url is an optional URL to an address for the named maintainer
Url string `protobuf:"bytes,3,opt,name=url" json:"url,omitempty"` Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *Maintainer) Reset() { *m = Maintainer{} } func (m *Maintainer) Reset() { *m = Maintainer{} }
func (m *Maintainer) String() string { return proto.CompactTextString(m) } func (m *Maintainer) String() string { return proto.CompactTextString(m) }
func (*Maintainer) ProtoMessage() {} func (*Maintainer) ProtoMessage() {}
func (*Maintainer) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0} } func (*Maintainer) Descriptor() ([]byte, []int) {
return fileDescriptor_metadata_bafee76586953fd5, []int{0}
}
func (m *Maintainer) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Maintainer.Unmarshal(m, b)
}
func (m *Maintainer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Maintainer.Marshal(b, m, deterministic)
}
func (dst *Maintainer) XXX_Merge(src proto.Message) {
xxx_messageInfo_Maintainer.Merge(dst, src)
}
func (m *Maintainer) XXX_Size() int {
return xxx_messageInfo_Maintainer.Size(m)
}
func (m *Maintainer) XXX_DiscardUnknown() {
xxx_messageInfo_Maintainer.DiscardUnknown(m)
}
var xxx_messageInfo_Maintainer proto.InternalMessageInfo
func (m *Maintainer) GetName() string { func (m *Maintainer) GetName() string {
if m != nil { if m != nil {
@ -74,47 +104,69 @@ func (m *Maintainer) GetUrl() string {
// Spec: https://k8s.io/helm/blob/master/docs/design/chart_format.md#the-chart-file // Spec: https://k8s.io/helm/blob/master/docs/design/chart_format.md#the-chart-file
type Metadata struct { type Metadata struct {
// The name of the chart // The name of the chart
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// The URL to a relevant project page, git repo, or contact person // The URL to a relevant project page, git repo, or contact person
Home string `protobuf:"bytes,2,opt,name=home" json:"home,omitempty"` Home string `protobuf:"bytes,2,opt,name=home,proto3" json:"home,omitempty"`
// Source is the URL to the source code of this chart // Source is the URL to the source code of this chart
Sources []string `protobuf:"bytes,3,rep,name=sources" json:"sources,omitempty"` Sources []string `protobuf:"bytes,3,rep,name=sources,proto3" json:"sources,omitempty"`
// A SemVer 2 conformant version string of the chart // A SemVer 2 conformant version string of the chart
Version string `protobuf:"bytes,4,opt,name=version" json:"version,omitempty"` Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"`
// A one-sentence description of the chart // A one-sentence description of the chart
Description string `protobuf:"bytes,5,opt,name=description" json:"description,omitempty"` Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"`
// A list of string keywords // A list of string keywords
Keywords []string `protobuf:"bytes,6,rep,name=keywords" json:"keywords,omitempty"` Keywords []string `protobuf:"bytes,6,rep,name=keywords,proto3" json:"keywords,omitempty"`
// A list of name and URL/email address combinations for the maintainer(s) // A list of name and URL/email address combinations for the maintainer(s)
Maintainers []*Maintainer `protobuf:"bytes,7,rep,name=maintainers" json:"maintainers,omitempty"` Maintainers []*Maintainer `protobuf:"bytes,7,rep,name=maintainers,proto3" json:"maintainers,omitempty"`
// The name of the template engine to use. Defaults to 'gotpl'. // The name of the template engine to use. Defaults to 'gotpl'.
Engine string `protobuf:"bytes,8,opt,name=engine" json:"engine,omitempty"` Engine string `protobuf:"bytes,8,opt,name=engine,proto3" json:"engine,omitempty"`
// The URL to an icon file. // The URL to an icon file.
Icon string `protobuf:"bytes,9,opt,name=icon" json:"icon,omitempty"` Icon string `protobuf:"bytes,9,opt,name=icon,proto3" json:"icon,omitempty"`
// The API Version of this chart. // The API Version of this chart.
ApiVersion string `protobuf:"bytes,10,opt,name=apiVersion" json:"apiVersion,omitempty"` ApiVersion string `protobuf:"bytes,10,opt,name=apiVersion,proto3" json:"apiVersion,omitempty"`
// The condition to check to enable chart // The condition to check to enable chart
Condition string `protobuf:"bytes,11,opt,name=condition" json:"condition,omitempty"` Condition string `protobuf:"bytes,11,opt,name=condition,proto3" json:"condition,omitempty"`
// The tags to check to enable chart // The tags to check to enable chart
Tags string `protobuf:"bytes,12,opt,name=tags" json:"tags,omitempty"` Tags string `protobuf:"bytes,12,opt,name=tags,proto3" json:"tags,omitempty"`
// The version of the application enclosed inside of this chart. // The version of the application enclosed inside of this chart.
AppVersion string `protobuf:"bytes,13,opt,name=appVersion" json:"appVersion,omitempty"` AppVersion string `protobuf:"bytes,13,opt,name=appVersion,proto3" json:"appVersion,omitempty"`
// Whether or not this chart is deprecated // Whether or not this chart is deprecated
Deprecated bool `protobuf:"varint,14,opt,name=deprecated" json:"deprecated,omitempty"` Deprecated bool `protobuf:"varint,14,opt,name=deprecated,proto3" json:"deprecated,omitempty"`
// TillerVersion is a SemVer constraints on what version of Tiller is required. // TillerVersion is a SemVer constraints on what version of Tiller is required.
// See SemVer ranges here: https://github.com/Masterminds/semver#basic-comparisons // See SemVer ranges here: https://github.com/Masterminds/semver#basic-comparisons
TillerVersion string `protobuf:"bytes,15,opt,name=tillerVersion" json:"tillerVersion,omitempty"` TillerVersion string `protobuf:"bytes,15,opt,name=tillerVersion,proto3" json:"tillerVersion,omitempty"`
// Annotations are additional mappings uninterpreted by Tiller, // Annotations are additional mappings uninterpreted by Tiller,
// made available for inspection by other applications. // made available for inspection by other applications.
Annotations map[string]string `protobuf:"bytes,16,rep,name=annotations" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Annotations map[string]string `protobuf:"bytes,16,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
// KubeVersion is a SemVer constraint specifying the version of Kubernetes required. // KubeVersion is a SemVer constraint specifying the version of Kubernetes required.
KubeVersion string `protobuf:"bytes,17,opt,name=kubeVersion" json:"kubeVersion,omitempty"` KubeVersion string `protobuf:"bytes,17,opt,name=kubeVersion,proto3" json:"kubeVersion,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *Metadata) Reset() { *m = Metadata{} } func (m *Metadata) Reset() { *m = Metadata{} }
func (m *Metadata) String() string { return proto.CompactTextString(m) } func (m *Metadata) String() string { return proto.CompactTextString(m) }
func (*Metadata) ProtoMessage() {} func (*Metadata) ProtoMessage() {}
func (*Metadata) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{1} } func (*Metadata) Descriptor() ([]byte, []int) {
return fileDescriptor_metadata_bafee76586953fd5, []int{1}
}
func (m *Metadata) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Metadata.Unmarshal(m, b)
}
func (m *Metadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Metadata.Marshal(b, m, deterministic)
}
func (dst *Metadata) XXX_Merge(src proto.Message) {
xxx_messageInfo_Metadata.Merge(dst, src)
}
func (m *Metadata) XXX_Size() int {
return xxx_messageInfo_Metadata.Size(m)
}
func (m *Metadata) XXX_DiscardUnknown() {
xxx_messageInfo_Metadata.DiscardUnknown(m)
}
var xxx_messageInfo_Metadata proto.InternalMessageInfo
func (m *Metadata) GetName() string { func (m *Metadata) GetName() string {
if m != nil { if m != nil {
@ -238,12 +290,13 @@ func (m *Metadata) GetKubeVersion() string {
func init() { func init() {
proto.RegisterType((*Maintainer)(nil), "hapi.chart.Maintainer") proto.RegisterType((*Maintainer)(nil), "hapi.chart.Maintainer")
proto.RegisterType((*Metadata)(nil), "hapi.chart.Metadata") proto.RegisterType((*Metadata)(nil), "hapi.chart.Metadata")
proto.RegisterMapType((map[string]string)(nil), "hapi.chart.Metadata.AnnotationsEntry")
proto.RegisterEnum("hapi.chart.Metadata_Engine", Metadata_Engine_name, Metadata_Engine_value) proto.RegisterEnum("hapi.chart.Metadata_Engine", Metadata_Engine_name, Metadata_Engine_value)
} }
func init() { proto.RegisterFile("hapi/chart/metadata.proto", fileDescriptor2) } func init() { proto.RegisterFile("hapi/chart/metadata.proto", fileDescriptor_metadata_bafee76586953fd5) }
var fileDescriptor2 = []byte{ var fileDescriptor_metadata_bafee76586953fd5 = []byte{
// 435 bytes of a gzipped FileDescriptorProto // 435 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x52, 0x5d, 0x6b, 0xd4, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x52, 0x5d, 0x6b, 0xd4, 0x40,
0x14, 0x35, 0xcd, 0x66, 0x77, 0x73, 0x63, 0x35, 0x0e, 0x52, 0xc6, 0x22, 0x12, 0x16, 0x85, 0x7d, 0x14, 0x35, 0xcd, 0x66, 0x77, 0x73, 0x63, 0x35, 0x0e, 0x52, 0xc6, 0x22, 0x12, 0x16, 0x85, 0x7d,

View File

@ -12,21 +12,49 @@ var _ = proto.Marshal
var _ = fmt.Errorf var _ = fmt.Errorf
var _ = math.Inf var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// Template represents a template as a name/value pair. // Template represents a template as a name/value pair.
// //
// By convention, name is a relative path within the scope of the chart's // By convention, name is a relative path within the scope of the chart's
// base directory. // base directory.
type Template struct { type Template struct {
// Name is the path-like name of the template. // Name is the path-like name of the template.
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Data is the template as byte data. // Data is the template as byte data.
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *Template) Reset() { *m = Template{} } func (m *Template) Reset() { *m = Template{} }
func (m *Template) String() string { return proto.CompactTextString(m) } func (m *Template) String() string { return proto.CompactTextString(m) }
func (*Template) ProtoMessage() {} func (*Template) ProtoMessage() {}
func (*Template) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{0} } func (*Template) Descriptor() ([]byte, []int) {
return fileDescriptor_template_926c98477d6df5e9, []int{0}
}
func (m *Template) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Template.Unmarshal(m, b)
}
func (m *Template) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Template.Marshal(b, m, deterministic)
}
func (dst *Template) XXX_Merge(src proto.Message) {
xxx_messageInfo_Template.Merge(dst, src)
}
func (m *Template) XXX_Size() int {
return xxx_messageInfo_Template.Size(m)
}
func (m *Template) XXX_DiscardUnknown() {
xxx_messageInfo_Template.DiscardUnknown(m)
}
var xxx_messageInfo_Template proto.InternalMessageInfo
func (m *Template) GetName() string { func (m *Template) GetName() string {
if m != nil { if m != nil {
@ -46,9 +74,9 @@ func init() {
proto.RegisterType((*Template)(nil), "hapi.chart.Template") proto.RegisterType((*Template)(nil), "hapi.chart.Template")
} }
func init() { proto.RegisterFile("hapi/chart/template.proto", fileDescriptor3) } func init() { proto.RegisterFile("hapi/chart/template.proto", fileDescriptor_template_926c98477d6df5e9) }
var fileDescriptor3 = []byte{ var fileDescriptor_template_926c98477d6df5e9 = []byte{
// 107 bytes of a gzipped FileDescriptorProto // 107 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcc, 0x48, 0x2c, 0xc8, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcc, 0x48, 0x2c, 0xc8,
0xd4, 0x4f, 0xce, 0x48, 0x2c, 0x2a, 0xd1, 0x2f, 0x49, 0xcd, 0x2d, 0xc8, 0x49, 0x2c, 0x49, 0xd5, 0xd4, 0x4f, 0xce, 0x48, 0x2c, 0x2a, 0xd1, 0x2f, 0x49, 0xcd, 0x2d, 0xc8, 0x49, 0x2c, 0x49, 0xd5,

View File

@ -1,15 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// source: hapi/version/version.proto // source: hapi/version/version.proto
/*
Package version is a generated protocol buffer package.
It is generated from these files:
hapi/version/version.proto
It has these top-level messages:
Version
*/
package version package version
import proto "github.com/golang/protobuf/proto" import proto "github.com/golang/protobuf/proto"
@ -29,15 +20,37 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Version struct { type Version struct {
// Sem ver string for the version // Sem ver string for the version
SemVer string `protobuf:"bytes,1,opt,name=sem_ver,json=semVer" json:"sem_ver,omitempty"` SemVer string `protobuf:"bytes,1,opt,name=sem_ver,json=semVer,proto3" json:"sem_ver,omitempty"`
GitCommit string `protobuf:"bytes,2,opt,name=git_commit,json=gitCommit" json:"git_commit,omitempty"` GitCommit string `protobuf:"bytes,2,opt,name=git_commit,json=gitCommit,proto3" json:"git_commit,omitempty"`
GitTreeState string `protobuf:"bytes,3,opt,name=git_tree_state,json=gitTreeState" json:"git_tree_state,omitempty"` GitTreeState string `protobuf:"bytes,3,opt,name=git_tree_state,json=gitTreeState,proto3" json:"git_tree_state,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *Version) Reset() { *m = Version{} } func (m *Version) Reset() { *m = Version{} }
func (m *Version) String() string { return proto.CompactTextString(m) } func (m *Version) String() string { return proto.CompactTextString(m) }
func (*Version) ProtoMessage() {} func (*Version) ProtoMessage() {}
func (*Version) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (*Version) Descriptor() ([]byte, []int) {
return fileDescriptor_version_10859f2d56ed17fa, []int{0}
}
func (m *Version) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Version.Unmarshal(m, b)
}
func (m *Version) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Version.Marshal(b, m, deterministic)
}
func (dst *Version) XXX_Merge(src proto.Message) {
xxx_messageInfo_Version.Merge(dst, src)
}
func (m *Version) XXX_Size() int {
return xxx_messageInfo_Version.Size(m)
}
func (m *Version) XXX_DiscardUnknown() {
xxx_messageInfo_Version.DiscardUnknown(m)
}
var xxx_messageInfo_Version proto.InternalMessageInfo
func (m *Version) GetSemVer() string { func (m *Version) GetSemVer() string {
if m != nil { if m != nil {
@ -64,9 +77,9 @@ func init() {
proto.RegisterType((*Version)(nil), "hapi.version.Version") proto.RegisterType((*Version)(nil), "hapi.version.Version")
} }
func init() { proto.RegisterFile("hapi/version/version.proto", fileDescriptor0) } func init() { proto.RegisterFile("hapi/version/version.proto", fileDescriptor_version_10859f2d56ed17fa) }
var fileDescriptor0 = []byte{ var fileDescriptor_version_10859f2d56ed17fa = []byte{
// 151 bytes of a gzipped FileDescriptorProto // 151 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xca, 0x48, 0x2c, 0xc8, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xca, 0x48, 0x2c, 0xc8,
0xd4, 0x2f, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0x83, 0xd1, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0xd4, 0x2f, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0x83, 0xd1, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9,

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@ -122,7 +122,7 @@ func NewFromKeyring(keyringfile, id string) (*Signatory, error) {
return s, nil return s, nil
} }
// We're gonna go all GnuPG on this and look for a string that _contains_. If // We're going to go all GnuPG on this and look for a string that _contains_. If
// two or more keys contain the string and none are a direct match, we error // two or more keys contain the string and none are a direct match, we error
// out. // out.
var candidate *openpgp.Entity var candidate *openpgp.Entity
@ -404,6 +404,8 @@ func DigestFile(filename string) (string, error) {
// Helm uses SHA256 as its default hash for all non-cryptographic applications. // Helm uses SHA256 as its default hash for all non-cryptographic applications.
func Digest(in io.Reader) (string, error) { func Digest(in io.Reader) (string, error) {
hash := crypto.SHA256.New() hash := crypto.SHA256.New()
io.Copy(hash, in) if _, err := io.Copy(hash, in); err != nil {
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil return hex.EncodeToString(hash.Sum(nil)), nil
} }

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -21,6 +21,7 @@ import (
"io/ioutil" "io/ioutil"
"net/url" "net/url"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
@ -111,14 +112,13 @@ func (r *ChartRepository) Load() error {
// cachePath is prepended to any index that does not have an absolute path. This // cachePath is prepended to any index that does not have an absolute path. This
// is for pre-2.2.0 repo files. // is for pre-2.2.0 repo files.
func (r *ChartRepository) DownloadIndexFile(cachePath string) error { func (r *ChartRepository) DownloadIndexFile(cachePath string) error {
var indexURL string
parsedURL, err := url.Parse(r.Config.URL) parsedURL, err := url.Parse(r.Config.URL)
if err != nil { if err != nil {
return err return err
} }
parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml" parsedURL.RawPath = path.Join(parsedURL.RawPath, "index.yaml")
parsedURL.Path = path.Join(parsedURL.Path, "index.yaml")
indexURL = parsedURL.String() indexURL := parsedURL.String()
r.setCredentials() r.setCredentials()
resp, err := r.Client.Get(indexURL) resp, err := r.Client.Get(indexURL)
@ -270,5 +270,15 @@ func ResolveReferenceURL(baseURL, refURL string) (string, error) {
return "", fmt.Errorf("failed to parse %s as URL: %v", refURL, err) return "", fmt.Errorf("failed to parse %s as URL: %v", refURL, err)
} }
return parsedBaseURL.ResolveReference(parsedRefURL).String(), nil // We need a trailing slash for ResolveReference to work, but make sure there isn't already one
parsedBaseURL.Path = strings.TrimSuffix(parsedBaseURL.Path, "/") + "/"
resolvedURL := parsedBaseURL.ResolveReference(parsedRefURL)
// if the base URL contains query string parameters,
// propagate them to the child URL but only if the
// refURL is relative to baseURL
if (resolvedURL.Hostname() == parsedBaseURL.Hostname()) && (resolvedURL.Port() == parsedBaseURL.Port()) {
resolvedURL.RawQuery = parsedBaseURL.RawQuery
}
return resolvedURL.String(), nil
} }

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
@ -110,7 +111,7 @@ func (i IndexFile) Add(md *chart.Metadata, filename, baseURL, digest string) {
_, file := filepath.Split(filename) _, file := filepath.Split(filename)
u, err = urlutil.URLJoin(baseURL, file) u, err = urlutil.URLJoin(baseURL, file)
if err != nil { if err != nil {
u = filepath.Join(baseURL, file) u = path.Join(baseURL, file)
} }
} }
cr := &ChartVersion{ cr := &ChartVersion{
@ -146,7 +147,8 @@ func (i IndexFile) SortEntries() {
// Get returns the ChartVersion for the given name. // Get returns the ChartVersion for the given name.
// //
// If version is empty, this will return the chart with the highest version. // If version is empty, this will return the chart with the latest stable version,
// prerelease versions will be skipped.
func (i IndexFile) Get(name, version string) (*ChartVersion, error) { func (i IndexFile) Get(name, version string) (*ChartVersion, error) {
vs, ok := i.Entries[name] vs, ok := i.Entries[name]
if !ok { if !ok {
@ -167,6 +169,15 @@ func (i IndexFile) Get(name, version string) (*ChartVersion, error) {
} }
} }
// when customer input exact version, check whether have exact match one first
if len(version) != 0 {
for _, ver := range vs {
if version == ver.Version {
return ver, nil
}
}
}
for _, ver := range vs { for _, ver := range vs {
test, err := semver.NewVersion(ver.Version) test, err := semver.NewVersion(ver.Version)
if err != nil { if err != nil {
@ -246,9 +257,11 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) {
var parentDir string var parentDir string
parentDir, fname = filepath.Split(fname) parentDir, fname = filepath.Split(fname)
// filepath.Split appends an extra slash to the end of parentDir. We want to strip that out.
parentDir = strings.TrimSuffix(parentDir, string(os.PathSeparator))
parentURL, err := urlutil.URLJoin(baseURL, parentDir) parentURL, err := urlutil.URLJoin(baseURL, parentDir)
if err != nil { if err != nil {
parentURL = filepath.Join(baseURL, parentDir) parentURL = path.Join(baseURL, parentDir)
} }
c, err := chartutil.Load(arch) c, err := chartutil.Load(arch)

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -117,12 +117,18 @@ func (r *RepoFile) Update(re ...*Entry) {
// Has returns true if the given name is already a repository name. // Has returns true if the given name is already a repository name.
func (r *RepoFile) Has(name string) bool { func (r *RepoFile) Has(name string) bool {
for _, rf := range r.Repositories { _, ok := r.Get(name)
if rf.Name == name { return ok
return true }
// Get returns entry by the given name if it exists.
func (r *RepoFile) Get(name string) (*Entry, bool) {
for _, entry := range r.Repositories {
if entry.Name == name {
return entry, true
} }
} }
return false return nil, false
} }
// Remove removes the entry from the list of repositories. // Remove removes the entry from the list of repositories.

View File

@ -4,7 +4,7 @@ the BSD license.
https://github.com/golang/go/blob/master/LICENSE https://github.com/golang/go/blob/master/LICENSE
Copyright 2017 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@ -22,6 +22,7 @@ package sympath
import ( import (
"fmt" "fmt"
"log"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
@ -69,12 +70,14 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
if err != nil { if err != nil {
return fmt.Errorf("error evaluating symlink %s: %s", path, err) return fmt.Errorf("error evaluating symlink %s: %s", path, err)
} }
log.Printf("found symbolic link in path: %s resolves to %s", path, resolved)
if info, err = os.Lstat(resolved); err != nil { if info, err = os.Lstat(resolved); err != nil {
return err return err
} }
if err := symwalk(resolved, info, walkFn); err != nil && err != filepath.SkipDir { if err := symwalk(path, info, walkFn); err != nil && err != filepath.SkipDir {
return err return err
} }
return nil
} }
if err := walkFn(path, info, nil); err != nil { if err := walkFn(path, info, nil); err != nil {

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -33,11 +33,14 @@ type Options struct {
CertFile string CertFile string
// Client-only options // Client-only options
InsecureSkipVerify bool InsecureSkipVerify bool
// Overrides the server name used to verify the hostname on the returned
// certificates from the server.
ServerName string
// Server-only options // Server-only options
ClientAuth tls.ClientAuthType ClientAuth tls.ClientAuthType
} }
// ClientConfig retusn a TLS configuration for use by a Helm client. // ClientConfig returns a TLS configuration for use by a Helm client.
func ClientConfig(opts Options) (cfg *tls.Config, err error) { func ClientConfig(opts Options) (cfg *tls.Config, err error) {
var cert *tls.Certificate var cert *tls.Certificate
var pool *x509.CertPool var pool *x509.CertPool
@ -55,8 +58,12 @@ func ClientConfig(opts Options) (cfg *tls.Config, err error) {
return nil, err return nil, err
} }
} }
cfg = &tls.Config{
cfg = &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify, Certificates: []tls.Certificate{*cert}, RootCAs: pool} InsecureSkipVerify: opts.InsecureSkipVerify,
Certificates: []tls.Certificate{*cert},
ServerName: opts.ServerName,
RootCAs: pool,
}
return cfg, nil return cfg, nil
} }

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -21,17 +21,20 @@ import (
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"k8s.io/helm/pkg/urlutil"
) )
// NewClientTLS returns tls.Config appropriate for client auth. func newTLSConfigCommon(certFile, keyFile, caFile string) (*tls.Config, error) {
func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) { config := tls.Config{}
cert, err := CertFromFilePair(certFile, keyFile)
if err != nil { if certFile != "" && keyFile != "" {
return nil, err cert, err := CertFromFilePair(certFile, keyFile)
} if err != nil {
config := tls.Config{ return nil, err
Certificates: []tls.Certificate{*cert}, }
config.Certificates = []tls.Certificate{*cert}
} }
if caFile != "" { if caFile != "" {
cp, err := CertPoolFromFile(caFile) cp, err := CertPoolFromFile(caFile)
if err != nil { if err != nil {
@ -39,9 +42,32 @@ func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
} }
config.RootCAs = cp config.RootCAs = cp
} }
return &config, nil return &config, nil
} }
// NewClientTLS returns tls.Config appropriate for client auth.
func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
return newTLSConfigCommon(certFile, keyFile, caFile)
}
// NewTLSConfig returns tls.Config appropriate for client and/or server auth.
func NewTLSConfig(url, certFile, keyFile, caFile string) (*tls.Config, error) {
config, err := newTLSConfigCommon(certFile, keyFile, caFile)
if err != nil {
return nil, err
}
config.BuildNameToCertificate()
serverName, err := urlutil.ExtractHostname(url)
if err != nil {
return nil, err
}
config.ServerName = serverName
return config, nil
}
// CertPoolFromFile returns an x509.CertPool containing the certificates // CertPoolFromFile returns an x509.CertPool containing the certificates
// in the given PEM-encoded file. // in the given PEM-encoded file.
// Returns an error if the file could not be read, a certificate could not // Returns an error if the file could not be read, a certificate could not

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -73,7 +73,7 @@ func ExtractHostname(addr string) (string, error) {
return stripPort(u.Host), nil return stripPort(u.Host), nil
} }
// Backported from Go 1.8 because Circle is still on 1.7 // stripPort from Go 1.8 because Circle is still on 1.7
func stripPort(hostport string) string { func stripPort(hostport string) string {
colon := strings.IndexByte(hostport, ':') colon := strings.IndexByte(hostport, ':')
if colon == -1 { if colon == -1 {

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016 The Kubernetes Authors All rights reserved. Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -26,7 +26,7 @@ var (
// Increment major number for new feature additions and behavioral changes. // Increment major number for new feature additions and behavioral changes.
// Increment minor number for bug fixes and performance enhancements. // Increment minor number for bug fixes and performance enhancements.
// Increment patch number for critical fixes to existing releases. // Increment patch number for critical fixes to existing releases.
Version = "v2.9" Version = "v2.16"
// BuildMetadata is extra build time data // BuildMetadata is extra build time data
BuildMetadata = "unreleased" BuildMetadata = "unreleased"

View File

@ -92,6 +92,8 @@ github.com/casbin/casbin/rbac/default-role-manager
github.com/casbin/casbin/config github.com/casbin/casbin/config
# github.com/coreos/go-oidc v2.0.0+incompatible # github.com/coreos/go-oidc v2.0.0+incompatible
github.com/coreos/go-oidc github.com/coreos/go-oidc
# github.com/cyphar/filepath-securejoin v0.2.2
github.com/cyphar/filepath-securejoin
# github.com/davecgh/go-spew v1.1.1 # github.com/davecgh/go-spew v1.1.1
github.com/davecgh/go-spew/spew github.com/davecgh/go-spew/spew
# github.com/dghubble/sling v1.1.0 # github.com/dghubble/sling v1.1.0
@ -416,7 +418,7 @@ k8s.io/client-go/pkg/apis/clientauthentication/v1beta1
k8s.io/client-go/util/connrotation k8s.io/client-go/util/connrotation
k8s.io/client-go/util/integer k8s.io/client-go/util/integer
k8s.io/client-go/util/homedir k8s.io/client-go/util/homedir
# k8s.io/helm v2.9.1+incompatible # k8s.io/helm v2.16.1+incompatible
k8s.io/helm/cmd/helm/search k8s.io/helm/cmd/helm/search
k8s.io/helm/pkg/chartutil k8s.io/helm/pkg/chartutil
k8s.io/helm/pkg/proto/hapi/chart k8s.io/helm/pkg/proto/hapi/chart