1
0
mirror of https://github.com/goharbor/harbor.git synced 2024-12-30 04:28:17 +01:00

Merge pull request from wy65701436/chart-details

Get addition properties for chart
This commit is contained in:
Wang Yan 2020-02-17 13:36:24 +08:00 committed by GitHub
commit e5eb711827
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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"
"github.com/goharbor/harbor/src/api/artifact/descriptor"
"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/chart"
"github.com/goharbor/harbor/src/pkg/repository"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
@ -31,7 +33,7 @@ const (
// ArtifactTypeChart defines the artifact type for helm chart
ArtifactTypeChart = "CHART"
AdditionTypeValues = "VALUES.YAML"
AdditionTypeReadme = "README"
AdditionTypeReadme = "README.MD"
AdditionTypeDependencies = "DEPENDENCIES"
// TODO import it from helm chart repository
mediaType = "application/vnd.cncf.helm.config.v1+json"
@ -41,6 +43,7 @@ func init() {
resolver := &resolver{
repoMgr: repository.Mgr,
blobFetcher: blob.Fcher,
chartOperator: chart.Optr,
}
if err := resolv.Register(resolver, mediaType); err != nil {
log.Errorf("failed to register resolver for media type %s: %v", mediaType, err)
@ -55,6 +58,7 @@ func init() {
type resolver struct {
repoMgr repository.Manager
blobFetcher blob.Fetcher
chartOperator chart.Operator
}
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) {
// 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
}

View File

@ -16,10 +16,14 @@ package chart
import (
"github.com/goharbor/harbor/src/common/models"
ierror "github.com/goharbor/harbor/src/internal/error"
"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/pkg/chart"
"github.com/goharbor/harbor/src/testing/pkg/repository"
"github.com/stretchr/testify/suite"
"k8s.io/helm/pkg/chartutil"
"testing"
)
@ -28,14 +32,17 @@ type resolverTestSuite struct {
resolver *resolver
repoMgr *repository.FakeManager
blobFetcher *blob.FakeFetcher
chartOptr *chart.FakeOpertaor
}
func (r *resolverTestSuite) SetupTest() {
r.repoMgr = &repository.FakeManager{}
r.blobFetcher = &blob.FakeFetcher{}
r.chartOptr = &chart.FakeOpertaor{}
r.resolver = &resolver{
repoMgr: r.repoMgr,
blobFetcher: r.blobFetcher,
chartOperator: r.chartOptr,
}
}
@ -95,6 +102,86 @@ func (r *resolverTestSuite) TestResolveMetadata() {
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() {
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/cloudflare/cfssl v0.0.0-20190510060611-9c027c93ba9e // indirect
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/dgrijalva/jwt-go v3.2.0+incompatible
github.com/docker/distribution v2.7.1+incompatible
@ -88,5 +89,5 @@ require (
k8s.io/api v0.0.0-20190222213804-5cb15d344471
k8s.io/apimachinery v0.0.0-20180704011316-f534d624797b
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/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/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
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/client-go v8.0.0+incompatible h1:tTI4hRmb1DRMl4fG6Vclfdi6nTM82oIrTT7HfitmxC4=
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.9.1+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI=
k8s.io/helm v2.16.1+incompatible h1:L+k810plJlaGWEw1EszeT4deK8XVaKxac1oGcuB+WDc=
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
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");
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");
you may not use this file except in compliance with the License.
@ -33,6 +33,12 @@ import (
"k8s.io/helm/pkg/repo"
)
const (
sep = "\v"
// verSep is a separator for version fields in map keys.
verSep = "$$"
)
// Result is a search result.
//
// 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
}
const sep = "\v"
// NewIndex creats a new Index.
// NewIndex creates a new Index.
func NewIndex() *Index {
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.
func (i *Index) AddRepo(rname string, ind *repo.IndexFile, all bool) {
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");
you may not use this file except in compliance with the License.
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
limitations under the License.
*/
//go:generate go run generator/capabilities_default_versions_generate.go
package chartutil
@ -24,14 +25,15 @@ import (
)
var (
// DefaultVersionSet is the default version set, which includes only Core V1 ("v1").
DefaultVersionSet = NewVersionSet("v1")
// DefaultVersionSet is the default version set in included in Kubernetes for workloads
// Default versions as of Kubernetes 1.14
DefaultVersionSet = NewVersionSet(defaultVersions()...)
// DefaultKubeVersion is the default kubernetes version
DefaultKubeVersion = &version.Info{
Major: "1",
Minor: "9",
GitVersion: "v1.9.0",
Minor: "14",
GitVersion: "v1.14.0",
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
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.
type Capabilities struct {
// List of all supported API versions
// APIVersions list of all supported API versions
APIVersions VersionSet
// KubeVerison is the Kubernetes version
// KubeVersion is the Kubernetes version
KubeVersion *version.Info
// 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");
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");
you may not use this file except in compliance with the License.
@ -42,10 +42,16 @@ const (
DeploymentName = "deployment.yaml"
// ServiceName is the name of the example service file.
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 = "NOTES.txt"
// HelpersName is the name of the example NOTES.txt file.
// HelpersName is the name of the example helpers file.
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.
@ -59,6 +65,28 @@ image:
tag: stable
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:
type: ClusterIP
port: 80
@ -68,9 +96,10 @@ ingress:
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
path: /
hosts:
- chart-example.local
- host: chart-example.local
paths: []
tls: []
# - secretName: chart-example-tls
# hosts:
@ -116,71 +145,81 @@ const defaultIgnore = `# Patterns to ignore when building packages.
.project
.idea/
*.tmproj
.vscode/
`
const defaultIngress = `{{- if .Values.ingress.enabled -}}
{{- $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
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
app: {{ template "<CHARTNAME>.name" . }}
chart: {{ template "<CHARTNAME>.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
{{ include "<CHARTNAME>.labels" . | indent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ . }}
- host: {{ .host | quote }}
http:
paths:
- path: {{ $ingressPath }}
{{- range .paths }}
- path: {{ . }}
backend:
serviceName: {{ $fullName }}
servicePort: http
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
`
const defaultDeployment = `apiVersion: apps/v1beta2
const defaultDeployment = `apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "<CHARTNAME>.fullname" . }}
name: {{ include "<CHARTNAME>.fullname" . }}
labels:
app: {{ template "<CHARTNAME>.name" . }}
chart: {{ template "<CHARTNAME>.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{ include "<CHARTNAME>.labels" . | indent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "<CHARTNAME>.name" . }}
release: {{ .Release.Name }}
app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "<CHARTNAME>.name" . }}
release: {{ .Release.Name }}
app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ template "<CHARTNAME>.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
@ -196,30 +235,27 @@ spec:
path: /
port: http
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- toYaml . | nindent 8 }}
{{- end }}
`
const defaultService = `apiVersion: v1
kind: Service
metadata:
name: {{ template "<CHARTNAME>.fullname" . }}
name: {{ include "<CHARTNAME>.fullname" . }}
labels:
app: {{ template "<CHARTNAME>.name" . }}
chart: {{ template "<CHARTNAME>.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{ include "<CHARTNAME>.labels" . | indent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
@ -228,26 +264,37 @@ spec:
protocol: TCP
name: http
selector:
app: {{ template "<CHARTNAME>.name" . }}
release: {{ .Release.Name }}
app.kubernetes.io/name: {{ include "<CHARTNAME>.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:
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }}
{{- end }}
{{- end }}
{{- 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}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
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" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "<CHARTNAME>.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
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 }} {{ include "<CHARTNAME>.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- 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"
kubectl port-forward $POD_NAME 8080:80
{{- end }}
@ -285,6 +332,47 @@ Create chart name and version as used by the chart label.
{{- define "<CHARTNAME>.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- 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.
@ -304,8 +392,9 @@ func CreateFrom(chartfile *chart.Metadata, dest string, src string) error {
}
schart.Templates = updatedTemplates
if schart.Values != nil {
schart.Values = &chart.Config{Raw: string(Transform(schart.Values.Raw, "<CHARTNAME>", schart.Metadata.Name))}
}
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 {
return cdir, err
}
@ -385,6 +474,11 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) {
path: filepath.Join(cdir, TemplatesDir, ServiceName),
content: Transform(defaultService, "<CHARTNAME>", chartfile.Name),
},
{
// serviceaccount.yaml
path: filepath.Join(cdir, TemplatesDir, ServiceAccountName),
content: Transform(defaultServiceAccount, "<CHARTNAME>", chartfile.Name),
},
{
// NOTES.txt
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),
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 {

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");
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.
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:
@ -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
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`:

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");
you may not use this file except in compliance with the License.
@ -17,58 +17,60 @@ limitations under the License.
package chartutil
import (
"archive/tar"
"compress/gzip"
"errors"
"io"
"io/ioutil"
"os"
"path/filepath"
securejoin "github.com/cyphar/filepath-securejoin"
)
// Expand uncompresses and extracts a chart into the specified directory.
func Expand(dir string, r io.Reader) error {
gr, err := gzip.NewReader(r)
files, err := loadArchiveFiles(r)
if err != nil {
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
d, _ := filepath.Split(header.Name)
fullDir := filepath.Join(dir, d)
_, err = os.Stat(fullDir)
if err != nil && d != "" {
if err := os.MkdirAll(fullDir, 0700); err != nil {
return err
}
}
path := filepath.Clean(filepath.Join(dir, header.Name))
info := header.FileInfo()
if info.IsDir() {
if err = os.MkdirAll(path, info.Mode()); err != nil {
return err
}
continue
}
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
// Get the name of the chart
var chartName string
for _, file := range files {
if file.Name == "Chart.yaml" {
ch, err := UnmarshalChartfile(file.Data)
if err != nil {
return err
}
_, err = io.Copy(file, tr)
chartName = ch.GetName()
}
}
if chartName == "" {
return errors.New("chart name not specified")
}
// Find the base directory
chartdir, err := securejoin.SecureJoin(dir, chartName)
if err != nil {
file.Close()
return err
}
file.Close()
// 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 {
return err
}
// Make sure the necessary subdirs get created.
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 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");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@ -36,11 +36,9 @@ type Files map[string][]byte
// Given an []*any.Any (the format for files in a chart.Chart), extract a map of files.
func NewFiles(from []*any.Any) Files {
files := map[string][]byte{}
if from != nil {
for _, f := range from {
files[f.TypeUrl] = f.Value
}
}
return files
}
@ -211,7 +209,8 @@ func ToToml(v interface{}) string {
// always return a string, even on marshal error (empty string).
//
// 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)
if err != nil {
// 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
// it tolerates errors. It will insert the returned error message string into
// 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{}{}
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");
you may not use this file except in compliance with the License.
@ -24,8 +24,11 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"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
// matching it. But .helmignore is not evaluated when reading out of an archive.
func Load(name string) (*chart.Chart, error) {
name = filepath.FromSlash(name)
fi, err := os.Stat(name)
if err != nil {
return nil, err
@ -62,11 +66,13 @@ type BufferedFile struct {
Data []byte
}
// LoadArchive loads from a reader containing a compressed tar archive.
func LoadArchive(in io.Reader) (*chart.Chart, error) {
var drivePathPattern = regexp.MustCompile(`^[a-zA-Z]:/`)
// loadArchiveFiles loads files out of an archive
func loadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
unzipped, err := gzip.NewReader(in)
if err != nil {
return &chart.Chart{}, err
return nil, err
}
defer unzipped.Close()
@ -79,7 +85,7 @@ func LoadArchive(in io.Reader) (*chart.Chart, error) {
break
}
if err != nil {
return &chart.Chart{}, err
return nil, err
}
if hd.FileInfo().IsDir() {
@ -88,6 +94,12 @@ func LoadArchive(in io.Reader) (*chart.Chart, error) {
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
delimiter := "/"
if strings.ContainsRune(hd.Name, '\\') {
@ -100,12 +112,33 @@ func LoadArchive(in io.Reader) (*chart.Chart, error) {
// Normalize the path to the / delimiter
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" {
return nil, errors.New("chart yaml not in base directory")
}
if _, err := io.Copy(b, tr); err != nil {
return &chart.Chart{}, err
return files, err
}
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 {
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)
}
@ -131,6 +172,10 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
return c, err
}
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" {
return c, errors.New("values.toml is illegal as of 2.0.0-alpha.2")
} else if f.Name == "values.yaml" {
@ -215,7 +260,45 @@ func LoadFile(name string) (*chart.Chart, error) {
}
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.
@ -272,6 +355,13 @@ func LoadDir(dir string) (*chart.Chart, error) {
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)
if err != nil {
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");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@ -45,7 +45,7 @@ var (
type Dependency struct {
// 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"`
// 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.
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"`
// Digest is a hash of the requirements file used to generate it.
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
func ProcessRequirementsConditions(reqs *Requirements, cvals Values) {
func ProcessRequirementsConditions(reqs *Requirements, cvals Values, cpath string) {
var cond string
var conds []string
if reqs == nil || len(reqs.Dependencies) == 0 {
@ -143,7 +143,7 @@ func ProcessRequirementsConditions(reqs *Requirements, cvals Values) {
for _, c := range conds {
if len(c) > 0 {
// retrieve value
vv, err := cvals.PathValue(c)
vv, err := cvals.PathValue(cpath + c)
if err == nil {
// if not bool, warn
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
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)
if err != nil {
// 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}
// flag dependencies as enabled/disabled
ProcessRequirementsTags(reqs, cvals)
ProcessRequirementsConditions(reqs, cvals)
ProcessRequirementsConditions(reqs, cvals, path)
// make a map of charts to remove
rm := map[string]bool{}
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
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 nerr, ok := err.(ErrNoRequirementsFile); !ok && err != nil {
return nerr
@ -391,9 +396,24 @@ func processImportValues(c *chart.Chart) error {
if err != nil {
return err
}
b := make(map[string]interface{}, 0)
b := cvals.AsMap()
// import values from each dependency if specified in import-values
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 {
var outiv []interface{}
for _, riv := range r.ImportValues {
@ -404,7 +424,7 @@ func processImportValues(c *chart.Chart) error {
"parent": iv["parent"].(string),
}
outiv = append(outiv, nm)
s := r.Name + "." + nm["child"]
s := name + "." + nm["child"]
// get child table
vv, err := cvals.Table(s)
if err != nil {
@ -413,27 +433,26 @@ func processImportValues(c *chart.Chart) error {
}
// create value map from child to be merged into parent
vm := pathToMap(nm["parent"], vv.AsMap())
b = coalesceTables(cvals, vm)
b = coalesceTables(b, vm, c.Metadata.Name)
case string:
nm := map[string]string{
"child": "exports." + iv,
"parent": ".",
}
outiv = append(outiv, nm)
s := r.Name + "." + nm["child"]
s := name + "." + nm["child"]
vm, err := cvals.Table(s)
if err != nil {
log.Printf("Warning: ImportValues missing table: %v", err)
continue
}
b = coalesceTables(b, vm.AsMap())
b = coalesceTables(b, vm.AsMap(), c.Metadata.Name)
}
}
// set our formatted import values
r.ImportValues = outiv
}
}
b = coalesceTables(b, cvals)
y, err := yaml.Marshal(b)
if err != nil {
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");
you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"time"
"github.com/ghodss/yaml"
@ -48,12 +49,12 @@ func SaveDir(c *chart.Chart, dest string) error {
// Save values.yaml
if c.Values != nil && len(c.Values.Raw) > 0 {
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
}
}
for _, d := range []string{TemplatesDir, ChartsDir} {
for _, d := range []string{TemplatesDir, ChartsDir, TemplatesTestsDir} {
if err := os.MkdirAll(filepath.Join(outdir, d), 0755); err != nil {
return err
}
@ -62,7 +63,13 @@ func SaveDir(c *chart.Chart, dest string) error {
// Save templates
for _, f := range c.Templates {
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
}
}
@ -76,7 +83,7 @@ func SaveDir(c *chart.Chart, dest string) error {
return err
}
if err := ioutil.WriteFile(n, f.Value, 0755); err != nil {
if err := ioutil.WriteFile(n, f.Value, 0644); err != nil {
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 {
// TODO: Do we need to create dummy parent directory names if none exist?
h := &tar.Header{
Name: name,
Name: filepath.ToSlash(name),
Mode: 0755,
Size: int64(len(body)),
ModTime: time.Now(),
}
if err := out.WriteHeader(h); err != nil {
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");
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");
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
}
// 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) {
v2, ok := v[simple]
if !ok {
@ -150,15 +166,10 @@ func CoalesceValues(chrt *chart.Chart, vals *chart.Config) (Values, error) {
if err != nil {
return cvals, err
}
cvals, err = coalesce(chrt, evals)
if err != nil {
return cvals, err
}
return coalesce(chrt, evals)
}
var err error
cvals, err = coalesceDeps(chrt, cvals)
return cvals, err
return coalesceDeps(chrt, cvals)
}
// 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 {
return dest, err
}
coalesceDeps(ch, dest)
return dest, nil
return coalesceDeps(ch, dest)
}
// 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{})
// Get globals out of dest and merge them into dvmap.
coalesceGlobals(dvmap, dest)
dvmap = coalesceGlobals(dvmap, dest, chrt.Metadata.Name)
var err error
// 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.
//
// 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{}
if destglob, ok := dest[GlobalKey]; !ok {
dg = map[string]interface{}{}
} 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
}
if srcglob, ok := src[GlobalKey]; !ok {
sg = map[string]interface{}{}
} 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
}
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
// 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
// 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{} {
dest := make(map[string]interface{}, len(src))
for k, v := range src {
dest[k] = v
}
return dest
rv[GlobalKey] = coalesceTables(sg, dg, chartName)
return rv
}
// 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.
// FIXME: We should log this error. It indicates that the YAML data
// 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 {
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
return coalesceTables(v, nv.AsMap(), c.Metadata.Name), nil
}
// coalesceTables merges a source map into a destination map.
//
// 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
// values.
rv := make(map[string]interface{})
for key, val := range src {
if istable(val) {
if innerdst, ok := dst[key]; !ok {
dst[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
dv, ok := dst[key]
if !ok { // if not in dst, then copy from src
rv[key] = val
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
}
return dst
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
}
}
// 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

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");
you may not use this file except in compliance with the License.
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");
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.
// 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.
func All(settings environment.EnvSettings) 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");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@ -23,11 +23,10 @@ import (
"strings"
"k8s.io/helm/pkg/tlsutil"
"k8s.io/helm/pkg/urlutil"
"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
type HttpGetter struct { //nolint
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
func NewHTTPGetter(URL, CertFile, KeyFile, CAFile string) (*HttpGetter, error) {
var client HttpGetter
if CertFile != "" && KeyFile != "" {
tlsConf, err := tlsutil.NewClientTLS(CertFile, KeyFile, CAFile)
if err != nil {
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,
tr := &http.Transport{
DisableCompression: true,
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
}

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");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@ -21,6 +21,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"k8s.io/helm/pkg/helm/environment"
"k8s.io/helm/pkg/plugin"
@ -62,13 +63,15 @@ type pluginGetter struct {
// Get runs downloader plugin command
func (p *pluginGetter) Get(href string) (*bytes.Buffer, error) {
argv := []string{p.certFile, p.keyFile, p.cAFile, href}
prog := exec.Command(filepath.Join(p.base, p.command), argv...)
commands := strings.Split(p.command, " ")
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)
prog.Env = os.Environ()
buf := bytes.NewBuffer(nil)
prog.Stdout = buf
prog.Stderr = os.Stderr
prog.Stdin = os.Stdin
if err := prog.Run(); err != nil {
if eerr, ok := err.(*exec.ExitError); ok {
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");
you may not use this file except in compliance with the License.
@ -32,6 +32,19 @@ import (
"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.
var DefaultHelmHome = filepath.Join(homedir.HomeDir(), ".helm")
@ -39,7 +52,7 @@ var DefaultHelmHome = filepath.Join(homedir.HomeDir(), ".helm")
type EnvSettings struct {
// TillerHost is the host and port of Tiller.
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
// TillerNamespace is the namespace in which Tiller runs.
TillerNamespace string
@ -49,16 +62,41 @@ type EnvSettings struct {
Debug bool
// KubeContext is the name of the kubeconfig context.
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.
func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) {
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.KubeContext, "kube-context", "", "name of the kubeconfig context to use")
fs.BoolVar(&s.Debug, "debug", false, "enable verbose output")
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")
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.KubeContext, "kube-context", "", "Name of the kubeconfig context to use")
fs.StringVar(&s.KubeConfig, "kubeconfig", "", "Absolute path of the kubeconfig file to be used")
fs.BoolVar(&s.Debug, "debug", false, "Enable verbose output")
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.
@ -68,12 +106,11 @@ func (s *EnvSettings) Init(fs *pflag.FlagSet) {
}
}
// PluginDirs is the path to the plugin directories.
func (s EnvSettings) PluginDirs() string {
if d, ok := os.LookupEnv("HELM_PLUGIN"); ok {
return d
// InitTLS sets TLS values from the environment.
func (s *EnvSettings) InitTLS(fs *pflag.FlagSet) {
for name, envar := range tlsEnvMap {
setFlagFromEnv(name, envar, fs)
}
return s.Home.Plugins()
}
// envMap maps flag names to envvars
@ -84,6 +121,34 @@ var envMap = map[string]string{
"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) {
if fs.Changed(name) {
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");
you may not use this file except in compliance with the License.
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");
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");
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)
}
// 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
// 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");
you may not use this file except in compliance with the License.
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");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

View File

@ -1,29 +1,12 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// 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
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
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.
var _ = proto.Marshal
@ -40,22 +23,44 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// optionally parameterizable templates, and zero or more charts (dependencies).
type Chart struct {
// 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 []*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.
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.
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,
// 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) String() string { return proto.CompactTextString(m) }
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 {
if m != nil {
@ -85,7 +90,7 @@ func (m *Chart) GetValues() *Config {
return nil
}
func (m *Chart) GetFiles() []*google_protobuf.Any {
func (m *Chart) GetFiles() []*any.Any {
if m != nil {
return m.Files
}
@ -96,9 +101,9 @@ func init() {
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
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,

View File

@ -12,16 +12,44 @@ var _ = proto.Marshal
var _ = fmt.Errorf
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.
type Config struct {
Raw string `protobuf:"bytes,1,opt,name=raw" 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"`
Raw string `protobuf:"bytes,1,opt,name=raw,proto3" json:"raw,omitempty"`
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) String() string { return proto.CompactTextString(m) }
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 {
if m != nil {
@ -39,13 +67,35 @@ func (m *Config) GetValues() map[string]*Value {
// Value describes a configuration value as a string.
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) String() string { return proto.CompactTextString(m) }
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 {
if m != nil {
@ -56,12 +106,13 @@ func (m *Value) GetValue() string {
func init() {
proto.RegisterType((*Config)(nil), "hapi.chart.Config")
proto.RegisterMapType((map[string]*Value)(nil), "hapi.chart.Config.ValuesEntry")
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
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,

View File

@ -12,6 +12,12 @@ var _ = proto.Marshal
var _ = fmt.Errorf
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
const (
@ -31,22 +37,46 @@ var Metadata_Engine_value = map[string]int32{
func (x Metadata_Engine) String() string {
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.
type Maintainer struct {
// 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 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 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) String() string { return proto.CompactTextString(m) }
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 {
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
type Metadata struct {
// 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
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
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
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
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
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)
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'.
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.
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.
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
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
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.
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
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.
// 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,
// 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 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) String() string { return proto.CompactTextString(m) }
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 {
if m != nil {
@ -238,12 +290,13 @@ func (m *Metadata) GetKubeVersion() string {
func init() {
proto.RegisterType((*Maintainer)(nil), "hapi.chart.Maintainer")
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)
}
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
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,

View File

@ -12,21 +12,49 @@ var _ = proto.Marshal
var _ = fmt.Errorf
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.
//
// By convention, name is a relative path within the scope of the chart's
// base directory.
type Template struct {
// 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 []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) String() string { return proto.CompactTextString(m) }
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 {
if m != nil {
@ -46,9 +74,9 @@ func init() {
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
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,

View File

@ -1,15 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// 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
import proto "github.com/golang/protobuf/proto"
@ -29,15 +20,37 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Version struct {
// Sem ver string for the version
SemVer string `protobuf:"bytes,1,opt,name=sem_ver,json=semVer" json:"sem_ver,omitempty"`
GitCommit string `protobuf:"bytes,2,opt,name=git_commit,json=gitCommit" json:"git_commit,omitempty"`
GitTreeState string `protobuf:"bytes,3,opt,name=git_tree_state,json=gitTreeState" json:"git_tree_state,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,proto3" json:"git_commit,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) String() string { return proto.CompactTextString(m) }
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 {
if m != nil {
@ -64,9 +77,9 @@ func init() {
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
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,

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");
you may not use this file except in compliance with the License.
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");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@ -122,7 +122,7 @@ func NewFromKeyring(keyringfile, id string) (*Signatory, error) {
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
// out.
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.
func Digest(in io.Reader) (string, error) {
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
}

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");
you may not use this file except in compliance with the License.
@ -21,6 +21,7 @@ import (
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"strings"
@ -111,14 +112,13 @@ func (r *ChartRepository) Load() error {
// cachePath is prepended to any index that does not have an absolute path. This
// is for pre-2.2.0 repo files.
func (r *ChartRepository) DownloadIndexFile(cachePath string) error {
var indexURL string
parsedURL, err := url.Parse(r.Config.URL)
if err != nil {
return err
}
parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml"
indexURL = parsedURL.String()
parsedURL.RawPath = path.Join(parsedURL.RawPath, "index.yaml")
parsedURL.Path = path.Join(parsedURL.Path, "index.yaml")
indexURL := parsedURL.String()
r.setCredentials()
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 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");
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");
you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"sort"
"strings"
@ -110,7 +111,7 @@ func (i IndexFile) Add(md *chart.Metadata, filename, baseURL, digest string) {
_, file := filepath.Split(filename)
u, err = urlutil.URLJoin(baseURL, file)
if err != nil {
u = filepath.Join(baseURL, file)
u = path.Join(baseURL, file)
}
}
cr := &ChartVersion{
@ -146,7 +147,8 @@ func (i IndexFile) SortEntries() {
// 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) {
vs, ok := i.Entries[name]
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 {
test, err := semver.NewVersion(ver.Version)
if err != nil {
@ -246,9 +257,11 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) {
var parentDir string
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)
if err != nil {
parentURL = filepath.Join(baseURL, parentDir)
parentURL = path.Join(baseURL, parentDir)
}
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");
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");
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.
func (r *RepoFile) Has(name string) bool {
for _, rf := range r.Repositories {
if rf.Name == name {
return true
_, ok := r.Get(name)
return ok
}
// 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.

View File

@ -4,7 +4,7 @@ the BSD 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");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@ -22,6 +22,7 @@ package sympath
import (
"fmt"
"log"
"os"
"path/filepath"
"sort"
@ -69,12 +70,14 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
if err != nil {
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 {
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 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");
you may not use this file except in compliance with the License.
@ -33,11 +33,14 @@ type Options struct {
CertFile string
// Client-only options
InsecureSkipVerify bool
// Overrides the server name used to verify the hostname on the returned
// certificates from the server.
ServerName string
// Server-only options
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) {
var cert *tls.Certificate
var pool *x509.CertPool
@ -55,8 +58,12 @@ func ClientConfig(opts Options) (cfg *tls.Config, err error) {
return nil, err
}
}
cfg = &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify, Certificates: []tls.Certificate{*cert}, RootCAs: pool}
cfg = &tls.Config{
InsecureSkipVerify: opts.InsecureSkipVerify,
Certificates: []tls.Certificate{*cert},
ServerName: opts.ServerName,
RootCAs: pool,
}
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");
you may not use this file except in compliance with the License.
@ -21,17 +21,20 @@ import (
"crypto/x509"
"fmt"
"io/ioutil"
"k8s.io/helm/pkg/urlutil"
)
// NewClientTLS returns tls.Config appropriate for client auth.
func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
func newTLSConfigCommon(certFile, keyFile, caFile string) (*tls.Config, error) {
config := tls.Config{}
if certFile != "" && keyFile != "" {
cert, err := CertFromFilePair(certFile, keyFile)
if err != nil {
return nil, err
}
config := tls.Config{
Certificates: []tls.Certificate{*cert},
config.Certificates = []tls.Certificate{*cert}
}
if caFile != "" {
cp, err := CertPoolFromFile(caFile)
if err != nil {
@ -39,9 +42,32 @@ func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
}
config.RootCAs = cp
}
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
// in the given PEM-encoded file.
// 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");
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
}
// 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 {
colon := strings.IndexByte(hostport, ':')
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");
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");
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");
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 minor number for bug fixes and performance enhancements.
// Increment patch number for critical fixes to existing releases.
Version = "v2.9"
Version = "v2.16"
// BuildMetadata is extra build time data
BuildMetadata = "unreleased"

View File

@ -92,6 +92,8 @@ github.com/casbin/casbin/rbac/default-role-manager
github.com/casbin/casbin/config
# github.com/coreos/go-oidc v2.0.0+incompatible
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/spew
# 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/integer
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/pkg/chartutil
k8s.io/helm/pkg/proto/hapi/chart