Upgrade k8s package verison (#5358)
* upgrade k8s package version Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io> * Script upgrade and code formatting. Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io> Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>
This commit is contained in:
@@ -1,368 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/internal/experimental/registry"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
orascontent "github.com/deislabs/oras/pkg/content"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
specs "github.com/opencontainers/image-spec/specs-go"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
)
|
||||
|
||||
const (
|
||||
// CacheRootDir is the root directory for a cache
|
||||
CacheRootDir = "cache"
|
||||
)
|
||||
|
||||
type (
|
||||
// Cache handles local/in-memory storage of Helm charts, compliant with OCI Layout
|
||||
Cache struct {
|
||||
debug bool
|
||||
out io.Writer
|
||||
rootDir string
|
||||
ociStore *orascontent.OCIStore
|
||||
memoryStore *orascontent.Memorystore
|
||||
}
|
||||
|
||||
// CacheRefSummary contains as much info as available describing a chart reference in cache
|
||||
// Note: fields here are sorted by the order in which they are set in FetchReference method
|
||||
CacheRefSummary struct {
|
||||
Name string
|
||||
Repo string
|
||||
Tag string
|
||||
Exists bool
|
||||
Manifest *ocispec.Descriptor
|
||||
Config *ocispec.Descriptor
|
||||
ContentLayer *ocispec.Descriptor
|
||||
Size int64
|
||||
Digest digest.Digest
|
||||
CreatedAt time.Time
|
||||
Chart *chart.Chart
|
||||
}
|
||||
)
|
||||
|
||||
// NewCache returns a new OCI Layout-compliant cache with config
|
||||
func NewCache(opts ...CacheOption) (*Cache, error) {
|
||||
cache := &Cache{
|
||||
out: ioutil.Discard,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(cache)
|
||||
}
|
||||
// validate
|
||||
if cache.rootDir == "" {
|
||||
return nil, errors.New("must set cache root dir on initialization")
|
||||
}
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
// FetchReference retrieves a chart ref from cache
|
||||
func (cache *Cache) FetchReference(ref *Reference) (*CacheRefSummary, error) {
|
||||
if err := cache.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := CacheRefSummary{
|
||||
Name: ref.FullName(),
|
||||
Repo: ref.Repo,
|
||||
Tag: ref.Tag,
|
||||
}
|
||||
for _, desc := range cache.ociStore.ListReferences() {
|
||||
if desc.Annotations[ocispec.AnnotationRefName] == r.Name {
|
||||
r.Exists = true
|
||||
manifestBytes, err := cache.fetchBlob(&desc)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
var manifest ocispec.Manifest
|
||||
err = json.Unmarshal(manifestBytes, &manifest)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
r.Manifest = &desc
|
||||
r.Config = &manifest.Config
|
||||
numLayers := len(manifest.Layers)
|
||||
if numLayers != 1 {
|
||||
return &r, errors.New(
|
||||
fmt.Sprintf("manifest does not contain exactly 1 layer (total: %d)", numLayers))
|
||||
}
|
||||
var contentLayer *ocispec.Descriptor
|
||||
for _, layer := range manifest.Layers {
|
||||
switch layer.MediaType {
|
||||
case HelmChartContentLayerMediaType:
|
||||
contentLayer = &layer
|
||||
}
|
||||
}
|
||||
if contentLayer == nil {
|
||||
return &r, errors.New(
|
||||
fmt.Sprintf("manifest does not contain a layer with mediatype %s", HelmChartContentLayerMediaType))
|
||||
}
|
||||
if contentLayer.Size == 0 {
|
||||
return &r, errors.New(
|
||||
fmt.Sprintf("manifest layer with mediatype %s is of size 0", HelmChartContentLayerMediaType))
|
||||
}
|
||||
r.ContentLayer = contentLayer
|
||||
info, err := cache.ociStore.Info(ctx(cache.out, cache.debug), contentLayer.Digest)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
r.Size = info.Size
|
||||
r.Digest = info.Digest
|
||||
r.CreatedAt = info.CreatedAt
|
||||
contentBytes, err := cache.fetchBlob(contentLayer)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
ch, err := loader.LoadArchive(bytes.NewBuffer(contentBytes))
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
r.Chart = ch
|
||||
}
|
||||
}
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// StoreReference stores a chart ref in cache
|
||||
func (cache *Cache) StoreReference(ref *Reference, ch *chart.Chart) (*CacheRefSummary, error) {
|
||||
if err := cache.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := CacheRefSummary{
|
||||
Name: ref.FullName(),
|
||||
Repo: ref.Repo,
|
||||
Tag: ref.Tag,
|
||||
Chart: ch,
|
||||
}
|
||||
existing, _ := cache.FetchReference(ref)
|
||||
r.Exists = existing.Exists
|
||||
config, _, err := cache.saveChartConfig(ch)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
r.Config = config
|
||||
contentLayer, _, err := cache.saveChartContentLayer(ch)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
r.ContentLayer = contentLayer
|
||||
info, err := cache.ociStore.Info(ctx(cache.out, cache.debug), contentLayer.Digest)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
r.Size = info.Size
|
||||
r.Digest = info.Digest
|
||||
r.CreatedAt = info.CreatedAt
|
||||
manifest, _, err := cache.saveChartManifest(config, contentLayer)
|
||||
if err != nil {
|
||||
return &r, err
|
||||
}
|
||||
r.Manifest = manifest
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// DeleteReference deletes a chart ref from cache
|
||||
// TODO: garbage collection, only manifest removed
|
||||
func (cache *Cache) DeleteReference(ref *Reference) (*CacheRefSummary, error) {
|
||||
if err := cache.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := cache.FetchReference(ref)
|
||||
if err != nil || !r.Exists {
|
||||
return r, err
|
||||
}
|
||||
cache.ociStore.DeleteReference(r.Name)
|
||||
err = cache.ociStore.SaveIndex()
|
||||
return r, err
|
||||
}
|
||||
|
||||
// ListReferences lists all chart refs in a cache
|
||||
func (cache *Cache) ListReferences() ([]*CacheRefSummary, error) {
|
||||
if err := cache.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var rr []*CacheRefSummary
|
||||
for _, desc := range cache.ociStore.ListReferences() {
|
||||
name := desc.Annotations[ocispec.AnnotationRefName]
|
||||
if name == "" {
|
||||
if cache.debug {
|
||||
fmt.Fprintf(cache.out, "warning: found manifest without name: %s", desc.Digest.Hex())
|
||||
}
|
||||
continue
|
||||
}
|
||||
ref, err := ParseReference(name)
|
||||
if err != nil {
|
||||
return rr, err
|
||||
}
|
||||
r, err := cache.FetchReference(ref)
|
||||
if err != nil {
|
||||
return rr, err
|
||||
}
|
||||
rr = append(rr, r)
|
||||
}
|
||||
return rr, nil
|
||||
}
|
||||
|
||||
// AddManifest provides a manifest to the cache index.json
|
||||
func (cache *Cache) AddManifest(ref *Reference, manifest *ocispec.Descriptor) error {
|
||||
if err := cache.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
cache.ociStore.AddReference(ref.FullName(), *manifest)
|
||||
err := cache.ociStore.SaveIndex()
|
||||
return err
|
||||
}
|
||||
|
||||
// Provider provides a valid containerd Provider
|
||||
func (cache *Cache) Provider() content.Provider {
|
||||
return content.Provider(cache.ociStore)
|
||||
}
|
||||
|
||||
// Ingester provides a valid containerd Ingester
|
||||
func (cache *Cache) Ingester() content.Ingester {
|
||||
return content.Ingester(cache.ociStore)
|
||||
}
|
||||
|
||||
// ProvideIngester provides a valid oras ProvideIngester
|
||||
func (cache *Cache) ProvideIngester() orascontent.ProvideIngester {
|
||||
return orascontent.ProvideIngester(cache.ociStore)
|
||||
}
|
||||
|
||||
// init creates files needed necessary for OCI layout store
|
||||
func (cache *Cache) init() error {
|
||||
if cache.ociStore == nil {
|
||||
ociStore, err := orascontent.NewOCIStore(cache.rootDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache.ociStore = ociStore
|
||||
cache.memoryStore = orascontent.NewMemoryStore()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveChartConfig stores the Chart.yaml as json blob and returns a descriptor
|
||||
func (cache *Cache) saveChartConfig(ch *chart.Chart) (*ocispec.Descriptor, bool, error) {
|
||||
configBytes, err := json.Marshal(ch.Metadata)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
configExists, err := cache.storeBlob(configBytes)
|
||||
if err != nil {
|
||||
return nil, configExists, err
|
||||
}
|
||||
descriptor := cache.memoryStore.Add("", HelmChartConfigMediaType, configBytes)
|
||||
return &descriptor, configExists, nil
|
||||
}
|
||||
|
||||
// saveChartContentLayer stores the chart as tarball blob and returns a descriptor
|
||||
func (cache *Cache) saveChartContentLayer(ch *chart.Chart) (*ocispec.Descriptor, bool, error) {
|
||||
destDir := filepath.Join(cache.rootDir, ".build")
|
||||
os.MkdirAll(destDir, 0755)
|
||||
tmpFile, err := chartutil.Save(ch, destDir)
|
||||
defer os.Remove(tmpFile)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "failed to save")
|
||||
}
|
||||
contentBytes, err := ioutil.ReadFile(tmpFile)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
contentExists, err := cache.storeBlob(contentBytes)
|
||||
if err != nil {
|
||||
return nil, contentExists, err
|
||||
}
|
||||
descriptor := cache.memoryStore.Add("", HelmChartContentLayerMediaType, contentBytes)
|
||||
return &descriptor, contentExists, nil
|
||||
}
|
||||
|
||||
// saveChartManifest stores the chart manifest as json blob and returns a descriptor
|
||||
func (cache *Cache) saveChartManifest(config *ocispec.Descriptor, contentLayer *ocispec.Descriptor) (*ocispec.Descriptor, bool, error) {
|
||||
manifest := ocispec.Manifest{
|
||||
Versioned: specs.Versioned{SchemaVersion: 2},
|
||||
Config: *config,
|
||||
Layers: []ocispec.Descriptor{*contentLayer},
|
||||
}
|
||||
manifestBytes, err := json.Marshal(manifest)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
manifestExists, err := cache.storeBlob(manifestBytes)
|
||||
if err != nil {
|
||||
return nil, manifestExists, err
|
||||
}
|
||||
descriptor := ocispec.Descriptor{
|
||||
MediaType: ocispec.MediaTypeImageManifest,
|
||||
Digest: digest.FromBytes(manifestBytes),
|
||||
Size: int64(len(manifestBytes)),
|
||||
}
|
||||
return &descriptor, manifestExists, nil
|
||||
}
|
||||
|
||||
// storeBlob stores a blob on filesystem
|
||||
func (cache *Cache) storeBlob(blobBytes []byte) (bool, error) {
|
||||
var exists bool
|
||||
writer, err := cache.ociStore.Store.Writer(ctx(cache.out, cache.debug),
|
||||
content.WithRef(digest.FromBytes(blobBytes).Hex()))
|
||||
if err != nil {
|
||||
return exists, err
|
||||
}
|
||||
_, err = writer.Write(blobBytes)
|
||||
if err != nil {
|
||||
return exists, err
|
||||
}
|
||||
err = writer.Commit(ctx(cache.out, cache.debug), 0, writer.Digest())
|
||||
if err != nil {
|
||||
if !errdefs.IsAlreadyExists(err) {
|
||||
return exists, err
|
||||
}
|
||||
exists = true
|
||||
}
|
||||
err = writer.Close()
|
||||
return exists, err
|
||||
}
|
||||
|
||||
// fetchBlob retrieves a blob from filesystem
|
||||
func (cache *Cache) fetchBlob(desc *ocispec.Descriptor) ([]byte, error) {
|
||||
reader, err := cache.ociStore.ReaderAt(ctx(cache.out, cache.debug), *desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
bytes := make([]byte, desc.Size)
|
||||
_, err = reader.ReadAt(bytes, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/internal/experimental/registry"
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type (
|
||||
// CacheOption allows specifying various settings configurable by the user for overriding the defaults
|
||||
// used when creating a new default cache
|
||||
CacheOption func(*Cache)
|
||||
)
|
||||
|
||||
// CacheOptDebug returns a function that sets the debug setting on cache options set
|
||||
func CacheOptDebug(debug bool) CacheOption {
|
||||
return func(cache *Cache) {
|
||||
cache.debug = debug
|
||||
}
|
||||
}
|
||||
|
||||
// CacheOptWriter returns a function that sets the writer setting on cache options set
|
||||
func CacheOptWriter(out io.Writer) CacheOption {
|
||||
return func(cache *Cache) {
|
||||
cache.out = out
|
||||
}
|
||||
}
|
||||
|
||||
// CacheOptRoot returns a function that sets the root directory setting on cache options set
|
||||
func CacheOptRoot(rootDir string) CacheOption {
|
||||
return func(cache *Cache) {
|
||||
cache.rootDir = rootDir
|
||||
}
|
||||
}
|
||||
@@ -1,336 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/internal/experimental/registry"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
auth "github.com/deislabs/oras/pkg/auth/docker"
|
||||
"github.com/deislabs/oras/pkg/content"
|
||||
"github.com/deislabs/oras/pkg/oras"
|
||||
"github.com/gosuri/uitable"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/helmpath"
|
||||
)
|
||||
|
||||
const (
|
||||
// CredentialsFileBasename is the filename for auth credentials file
|
||||
CredentialsFileBasename = "config.json"
|
||||
)
|
||||
|
||||
type (
|
||||
// Client works with OCI-compliant registries and local Helm chart cache
|
||||
Client struct {
|
||||
debug bool
|
||||
// path to repository config file e.g. ~/.docker/config.json
|
||||
credentialsFile string
|
||||
out io.Writer
|
||||
authorizer *Authorizer
|
||||
resolver *Resolver
|
||||
cache *Cache
|
||||
}
|
||||
)
|
||||
|
||||
// NewClient returns a new registry client with config
|
||||
func NewClient(opts ...ClientOption) (*Client, error) {
|
||||
client := &Client{
|
||||
out: ioutil.Discard,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(client)
|
||||
}
|
||||
// set defaults if fields are missing
|
||||
if client.credentialsFile == "" {
|
||||
client.credentialsFile = helmpath.CachePath("registry", CredentialsFileBasename)
|
||||
}
|
||||
if client.authorizer == nil {
|
||||
authClient, err := auth.NewClient(client.credentialsFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.authorizer = &Authorizer{
|
||||
Client: authClient,
|
||||
}
|
||||
}
|
||||
if client.resolver == nil {
|
||||
resolver, err := client.authorizer.Resolver(context.Background(), http.DefaultClient, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.resolver = &Resolver{
|
||||
Resolver: resolver,
|
||||
}
|
||||
}
|
||||
if client.cache == nil {
|
||||
cache, err := NewCache(
|
||||
CacheOptDebug(client.debug),
|
||||
CacheOptWriter(client.out),
|
||||
CacheOptRoot(helmpath.CachePath("registry", CacheRootDir)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.cache = cache
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Login logs into a registry
|
||||
func (c *Client) Login(hostname string, username string, password string, insecure bool) error {
|
||||
err := c.authorizer.Login(ctx(c.out, c.debug), hostname, username, password, insecure)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(c.out, "Login succeeded\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Logout logs out of a registry
|
||||
func (c *Client) Logout(hostname string) error {
|
||||
err := c.authorizer.Logout(ctx(c.out, c.debug), hostname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(c.out, "Logout succeeded")
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushChart uploads a chart to a registry
|
||||
func (c *Client) PushChart(ref *Reference) error {
|
||||
r, err := c.cache.FetchReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !r.Exists {
|
||||
return errors.New(fmt.Sprintf("Chart not found: %s", r.Name))
|
||||
}
|
||||
fmt.Fprintf(c.out, "The push refers to repository [%s]\n", r.Repo)
|
||||
c.printCacheRefSummary(r)
|
||||
layers := []ocispec.Descriptor{*r.ContentLayer}
|
||||
_, err = oras.Push(ctx(c.out, c.debug), c.resolver, r.Name, c.cache.Provider(), layers,
|
||||
oras.WithConfig(*r.Config), oras.WithNameValidation(nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s := ""
|
||||
numLayers := len(layers)
|
||||
if 1 < numLayers {
|
||||
s = "s"
|
||||
}
|
||||
fmt.Fprintf(c.out,
|
||||
"%s: pushed to remote (%d layer%s, %s total)\n", r.Tag, numLayers, s, byteCountBinary(r.Size))
|
||||
return nil
|
||||
}
|
||||
|
||||
// PullChart downloads a chart from a registry
|
||||
func (c *Client) PullChart(ref *Reference) (*bytes.Buffer, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
if ref.Tag == "" {
|
||||
return buf, errors.New("tag explicitly required")
|
||||
}
|
||||
|
||||
fmt.Fprintf(c.out, "%s: Pulling from %s\n", ref.Tag, ref.Repo)
|
||||
|
||||
store := content.NewMemoryStore()
|
||||
fullname := ref.FullName()
|
||||
_ = fullname
|
||||
_, layerDescriptors, err := oras.Pull(ctx(c.out, c.debug), c.resolver, ref.FullName(), store,
|
||||
oras.WithPullEmptyNameAllowed(),
|
||||
oras.WithAllowedMediaTypes(KnownMediaTypes()))
|
||||
if err != nil {
|
||||
return buf, err
|
||||
}
|
||||
|
||||
numLayers := len(layerDescriptors)
|
||||
if numLayers < 1 {
|
||||
return buf, errors.New(
|
||||
fmt.Sprintf("manifest does not contain at least 1 layer (total: %d)", numLayers))
|
||||
}
|
||||
|
||||
var contentLayer *ocispec.Descriptor
|
||||
for _, layer := range layerDescriptors {
|
||||
layer := layer
|
||||
switch layer.MediaType {
|
||||
case HelmChartContentLayerMediaType:
|
||||
contentLayer = &layer
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if contentLayer == nil {
|
||||
return buf, errors.New(
|
||||
fmt.Sprintf("manifest does not contain a layer with mediatype %s",
|
||||
HelmChartContentLayerMediaType))
|
||||
}
|
||||
|
||||
_, b, ok := store.Get(*contentLayer)
|
||||
if !ok {
|
||||
return buf, errors.Errorf("Unable to retrieve blob with digest %s", contentLayer.Digest)
|
||||
}
|
||||
|
||||
buf = bytes.NewBuffer(b)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// PullChartToCache pulls a chart from an OCI Registry to the Registry Cache.
|
||||
// This function is needed for `helm chart pull`, which is experimental and will be deprecated soon.
|
||||
// Likewise, the Registry cache will soon be deprecated as will this function.
|
||||
func (c *Client) PullChartToCache(ref *Reference) error {
|
||||
if ref.Tag == "" {
|
||||
return errors.New("tag explicitly required")
|
||||
}
|
||||
existing, err := c.cache.FetchReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(c.out, "%s: Pulling from %s\n", ref.Tag, ref.Repo)
|
||||
manifest, _, err := oras.Pull(ctx(c.out, c.debug), c.resolver, ref.FullName(), c.cache.Ingester(),
|
||||
oras.WithPullEmptyNameAllowed(),
|
||||
oras.WithAllowedMediaTypes(KnownMediaTypes()),
|
||||
oras.WithContentProvideIngester(c.cache.ProvideIngester()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.cache.AddManifest(ref, &manifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r, err := c.cache.FetchReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !r.Exists {
|
||||
return errors.New(fmt.Sprintf("Chart not found: %s", r.Name))
|
||||
}
|
||||
c.printCacheRefSummary(r)
|
||||
if !existing.Exists {
|
||||
fmt.Fprintf(c.out, "Status: Downloaded newer chart for %s\n", ref.FullName())
|
||||
} else {
|
||||
fmt.Fprintf(c.out, "Status: Chart is up to date for %s\n", ref.FullName())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SaveChart stores a copy of chart in local cache
|
||||
func (c *Client) SaveChart(ch *chart.Chart, ref *Reference) error {
|
||||
r, err := c.cache.StoreReference(ref, ch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.printCacheRefSummary(r)
|
||||
err = c.cache.AddManifest(ref, r.Manifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(c.out, "%s: saved\n", r.Tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadChart retrieves a chart object by reference
|
||||
func (c *Client) LoadChart(ref *Reference) (*chart.Chart, error) {
|
||||
r, err := c.cache.FetchReference(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !r.Exists {
|
||||
return nil, errors.New(fmt.Sprintf("Chart not found: %s", ref.FullName()))
|
||||
}
|
||||
c.printCacheRefSummary(r)
|
||||
return r.Chart, nil
|
||||
}
|
||||
|
||||
// RemoveChart deletes a locally saved chart
|
||||
func (c *Client) RemoveChart(ref *Reference) error {
|
||||
r, err := c.cache.DeleteReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !r.Exists {
|
||||
return errors.New(fmt.Sprintf("Chart not found: %s", ref.FullName()))
|
||||
}
|
||||
fmt.Fprintf(c.out, "%s: removed\n", r.Tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintChartTable prints a list of locally stored charts
|
||||
func (c *Client) PrintChartTable() error {
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = 60
|
||||
table.AddRow("REF", "NAME", "VERSION", "DIGEST", "SIZE", "CREATED")
|
||||
rows, err := c.getChartTableRows()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, row := range rows {
|
||||
table.AddRow(row...)
|
||||
}
|
||||
fmt.Fprintln(c.out, table.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// printCacheRefSummary prints out chart ref summary
|
||||
func (c *Client) printCacheRefSummary(r *CacheRefSummary) {
|
||||
fmt.Fprintf(c.out, "ref: %s\n", r.Name)
|
||||
fmt.Fprintf(c.out, "digest: %s\n", r.Manifest.Digest.Hex())
|
||||
fmt.Fprintf(c.out, "size: %s\n", byteCountBinary(r.Size))
|
||||
fmt.Fprintf(c.out, "name: %s\n", r.Chart.Metadata.Name)
|
||||
fmt.Fprintf(c.out, "version: %s\n", r.Chart.Metadata.Version)
|
||||
}
|
||||
|
||||
// getChartTableRows returns rows in uitable-friendly format
|
||||
func (c *Client) getChartTableRows() ([][]interface{}, error) {
|
||||
rr, err := c.cache.ListReferences()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refsMap := map[string]map[string]string{}
|
||||
for _, r := range rr {
|
||||
refsMap[r.Name] = map[string]string{
|
||||
"name": r.Chart.Metadata.Name,
|
||||
"version": r.Chart.Metadata.Version,
|
||||
"digest": shortDigest(r.Manifest.Digest.Hex()),
|
||||
"size": byteCountBinary(r.Size),
|
||||
"created": timeAgo(r.CreatedAt),
|
||||
}
|
||||
}
|
||||
// Sort and convert to format expected by uitable
|
||||
rows := make([][]interface{}, len(refsMap))
|
||||
keys := make([]string, 0, len(refsMap))
|
||||
for key := range refsMap {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for i, key := range keys {
|
||||
rows[i] = make([]interface{}, 6)
|
||||
rows[i][0] = key
|
||||
ref := refsMap[key]
|
||||
for j, k := range []string{"name", "version", "digest", "size", "created"} {
|
||||
rows[i][j+1] = ref[k]
|
||||
}
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/internal/experimental/registry"
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type (
|
||||
// ClientOption allows specifying various settings configurable by the user for overriding the defaults
|
||||
// used when creating a new default client
|
||||
ClientOption func(*Client)
|
||||
)
|
||||
|
||||
// ClientOptDebug returns a function that sets the debug setting on client options set
|
||||
func ClientOptDebug(debug bool) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.debug = debug
|
||||
}
|
||||
}
|
||||
|
||||
// ClientOptWriter returns a function that sets the writer setting on client options set
|
||||
func ClientOptWriter(out io.Writer) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.out = out
|
||||
}
|
||||
}
|
||||
|
||||
// ClientOptResolver returns a function that sets the resolver setting on client options set
|
||||
func ClientOptResolver(resolver *Resolver) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.resolver = resolver
|
||||
}
|
||||
}
|
||||
|
||||
// ClientOptAuthorizer returns a function that sets the authorizer setting on client options set
|
||||
func ClientOptAuthorizer(authorizer *Authorizer) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.authorizer = authorizer
|
||||
}
|
||||
}
|
||||
|
||||
// ClientOptCache returns a function that sets the cache setting on a client options set
|
||||
func ClientOptCache(cache *Cache) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.cache = cache
|
||||
}
|
||||
}
|
||||
|
||||
// ClientOptCredentialsFile returns a function that sets the cache setting on a client options set
|
||||
func ClientOptCredentialsFile(credentialsFile string) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.credentialsFile = credentialsFile
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/internal/experimental/registry"
|
||||
|
||||
const (
|
||||
// HelmChartConfigMediaType is the reserved media type for the Helm chart manifest config
|
||||
HelmChartConfigMediaType = "application/vnd.cncf.helm.config.v1+json"
|
||||
|
||||
// HelmChartContentLayerMediaType is the reserved media type for Helm chart package content
|
||||
HelmChartContentLayerMediaType = "application/tar+gzip"
|
||||
)
|
||||
|
||||
// KnownMediaTypes returns a list of layer mediaTypes that the Helm client knows about
|
||||
func KnownMediaTypes() []string {
|
||||
return []string{
|
||||
HelmChartConfigMediaType,
|
||||
HelmChartContentLayerMediaType,
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/internal/experimental/registry"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
validPortRegEx = regexp.MustCompile(`^([1-9]\d{0,3}|0|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$`) // adapted from https://stackoverflow.com/a/12968117
|
||||
// TODO: Currently we don't support digests, so we are only splitting on the
|
||||
// colon. However, when we add support for digests, we'll need to use the
|
||||
// regexp anyway to split on both colons and @, so leaving it like this for
|
||||
// now
|
||||
referenceDelimiter = regexp.MustCompile(`[:]`)
|
||||
errEmptyRepo = errors.New("parsed repo was empty")
|
||||
errTooManyColons = errors.New("ref may only contain a single colon character (:) unless specifying a port number")
|
||||
)
|
||||
|
||||
type (
|
||||
// Reference defines the main components of a reference specification
|
||||
Reference struct {
|
||||
Tag string
|
||||
Repo string
|
||||
}
|
||||
)
|
||||
|
||||
// ParseReference converts a string to a Reference
|
||||
func ParseReference(s string) (*Reference, error) {
|
||||
if s == "" {
|
||||
return nil, errEmptyRepo
|
||||
}
|
||||
// Split the components of the string on the colon or @, if it is more than 3,
|
||||
// immediately return an error. Other validation will be performed later in
|
||||
// the function
|
||||
splitComponents := fixSplitComponents(referenceDelimiter.Split(s, -1))
|
||||
if len(splitComponents) > 3 {
|
||||
return nil, errTooManyColons
|
||||
}
|
||||
|
||||
var ref *Reference
|
||||
switch len(splitComponents) {
|
||||
case 1:
|
||||
ref = &Reference{Repo: splitComponents[0]}
|
||||
case 2:
|
||||
ref = &Reference{Repo: splitComponents[0], Tag: splitComponents[1]}
|
||||
case 3:
|
||||
ref = &Reference{Repo: strings.Join(splitComponents[:2], ":"), Tag: splitComponents[2]}
|
||||
}
|
||||
|
||||
// ensure the reference is valid
|
||||
err := ref.validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
// FullName the full name of a reference (repo:tag)
|
||||
func (ref *Reference) FullName() string {
|
||||
if ref.Tag == "" {
|
||||
return ref.Repo
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", ref.Repo, ref.Tag)
|
||||
}
|
||||
|
||||
// validate makes sure the ref meets our criteria
|
||||
func (ref *Reference) validate() error {
|
||||
|
||||
err := ref.validateRepo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ref.validateNumColons()
|
||||
}
|
||||
|
||||
// validateRepo checks that the Repo field is non-empty
|
||||
func (ref *Reference) validateRepo() error {
|
||||
if ref.Repo == "" {
|
||||
return errEmptyRepo
|
||||
}
|
||||
// Makes sure the repo results in a parsable URL (similar to what is done
|
||||
// with containerd reference parsing)
|
||||
_, err := url.Parse("//" + ref.Repo)
|
||||
return err
|
||||
}
|
||||
|
||||
// validateNumColons ensures the ref only contains a single colon character (:)
|
||||
// (or potentially two, there might be a port number specified i.e. :5000)
|
||||
func (ref *Reference) validateNumColons() error {
|
||||
if strings.Contains(ref.Tag, ":") {
|
||||
return errTooManyColons
|
||||
}
|
||||
parts := strings.Split(ref.Repo, ":")
|
||||
lastIndex := len(parts) - 1
|
||||
if 1 < lastIndex {
|
||||
return errTooManyColons
|
||||
}
|
||||
if 0 < lastIndex {
|
||||
port := strings.Split(parts[lastIndex], "/")[0]
|
||||
if !isValidPort(port) {
|
||||
return errTooManyColons
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isValidPort returns whether or not a string looks like a valid port
|
||||
func isValidPort(s string) bool {
|
||||
return validPortRegEx.MatchString(s)
|
||||
}
|
||||
|
||||
// fixSplitComponents this will modify reference parts based on presence of port
|
||||
// Example: {localhost, 5000/x/y/z, 0.1.0} => {localhost:5000/x/y/z, 0.1.0}
|
||||
func fixSplitComponents(c []string) []string {
|
||||
if len(c) <= 1 {
|
||||
return c
|
||||
}
|
||||
possiblePortParts := strings.Split(c[1], "/")
|
||||
if _, err := strconv.Atoi(possiblePortParts[0]); err == nil {
|
||||
components := []string{strings.Join(c[:2], ":")}
|
||||
components = append(components, c[2:]...)
|
||||
return components
|
||||
}
|
||||
return c
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/internal/experimental/registry"
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/remotes"
|
||||
)
|
||||
|
||||
type (
|
||||
// Resolver provides remotes based on a locator
|
||||
Resolver struct {
|
||||
remotes.Resolver
|
||||
}
|
||||
)
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/internal/experimental/registry"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
orascontext "github.com/deislabs/oras/pkg/context"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// byteCountBinary produces a human-readable file size
|
||||
func byteCountBinary(b int64) string {
|
||||
const unit = 1024
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp])
|
||||
}
|
||||
|
||||
// shortDigest returns first 7 characters of a sha256 digest
|
||||
func shortDigest(digest string) string {
|
||||
if len(digest) == 64 {
|
||||
return digest[:7]
|
||||
}
|
||||
return digest
|
||||
}
|
||||
|
||||
// timeAgo returns a human-readable timestamp representing time that has passed
|
||||
func timeAgo(t time.Time) string {
|
||||
return units.HumanDuration(time.Now().UTC().Sub(t))
|
||||
}
|
||||
|
||||
// ctx retrieves a fresh context.
|
||||
// disable verbose logging coming from ORAS (unless debug is enabled)
|
||||
func ctx(out io.Writer, debug bool) context.Context {
|
||||
if !debug {
|
||||
return orascontext.Background()
|
||||
}
|
||||
ctx := orascontext.WithLoggerFromWriter(context.Background(), out)
|
||||
orascontext.GetLogger(ctx).Logger.SetLevel(logrus.DebugLevel)
|
||||
return ctx
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package resolver
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -28,25 +29,25 @@ import (
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/gates"
|
||||
"helm.sh/helm/v3/pkg/helmpath"
|
||||
"helm.sh/helm/v3/pkg/provenance"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI")
|
||||
|
||||
// Resolver resolves dependencies from semantic version ranges to a particular version.
|
||||
type Resolver struct {
|
||||
chartpath string
|
||||
cachepath string
|
||||
chartpath string
|
||||
cachepath string
|
||||
registryClient *registry.Client
|
||||
}
|
||||
|
||||
// New creates a new resolver for a given chart and a given helm home.
|
||||
func New(chartpath, cachepath string) *Resolver {
|
||||
// New creates a new resolver for a given chart, helm home and registry client.
|
||||
func New(chartpath, cachepath string, registryClient *registry.Client) *Resolver {
|
||||
return &Resolver{
|
||||
chartpath: chartpath,
|
||||
cachepath: cachepath,
|
||||
chartpath: chartpath,
|
||||
cachepath: cachepath,
|
||||
registryClient: registryClient,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +122,7 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string
|
||||
var version string
|
||||
var ok bool
|
||||
found := true
|
||||
if !strings.HasPrefix(d.Repository, "oci://") {
|
||||
if !registry.IsOCI(d.Repository) {
|
||||
repoIndex, err := repo.LoadIndexFile(filepath.Join(r.cachepath, helmpath.CacheIndexFile(repoName)))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "no cached repository for %s found. (try 'helm repo update')", repoName)
|
||||
@@ -134,9 +135,36 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string
|
||||
found = false
|
||||
} else {
|
||||
version = d.Version
|
||||
if !FeatureGateOCI.IsEnabled() {
|
||||
return nil, errors.Wrapf(FeatureGateOCI.Error(),
|
||||
"repository %s is an OCI registry", d.Repository)
|
||||
|
||||
// Check to see if an explicit version has been provided
|
||||
_, err := semver.NewVersion(version)
|
||||
|
||||
// Use an explicit version, otherwise search for tags
|
||||
if err == nil {
|
||||
vs = []*repo.ChartVersion{{
|
||||
Metadata: &chart.Metadata{
|
||||
Version: version,
|
||||
},
|
||||
}}
|
||||
|
||||
} else {
|
||||
// Retrieve list of tags for repository
|
||||
ref := fmt.Sprintf("%s/%s", strings.TrimPrefix(d.Repository, fmt.Sprintf("%s://", registry.OCIScheme)), d.Name)
|
||||
tags, err := r.registryClient.Tags(ref)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not retrieve list of tags for repository %s", d.Repository)
|
||||
}
|
||||
|
||||
vs = make(repo.ChartVersions, len(tags))
|
||||
for ti, t := range tags {
|
||||
// Mock chart version objects
|
||||
version := &repo.ChartVersion{
|
||||
Metadata: &chart.Metadata{
|
||||
Version: t,
|
||||
},
|
||||
}
|
||||
vs[ti] = version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +176,8 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string
|
||||
// The version are already sorted and hence the first one to satisfy the constraint is used
|
||||
for _, ver := range vs {
|
||||
v, err := semver.NewVersion(ver.Version)
|
||||
if err != nil || len(ver.URLs) == 0 {
|
||||
// OCI does not need URLs
|
||||
if err != nil || (!registry.IsOCI(d.Repository) && len(ver.URLs) == 0) {
|
||||
// Not a legit entry.
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error evaluating symlink %s", path)
|
||||
}
|
||||
log.Printf("found symbolic link in path: %s resolves to %s", path, resolved)
|
||||
log.Printf("found symbolic link in path: %s resolves to %s. Contents of linked file included and used", path, resolved)
|
||||
if info, err = os.Lstat(resolved); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ package fs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -119,7 +118,7 @@ func CopyDir(src, dst string) error {
|
||||
return errors.Wrapf(err, "cannot mkdir %s", dst)
|
||||
}
|
||||
|
||||
entries, err := ioutil.ReadDir(src)
|
||||
entries, err := os.ReadDir(src)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot read directory %s", dst)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !windows
|
||||
//go:build !windows
|
||||
|
||||
/*
|
||||
Copyright (c) for portions of rename.go are held by The Go Authors, 2016 and are provided under
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build windows
|
||||
//go:build windows
|
||||
|
||||
/*
|
||||
Copyright (c) for portions of rename_windows.go are held by The Go Authors, 2016 and are provided under
|
||||
|
||||
@@ -29,7 +29,7 @@ var (
|
||||
//
|
||||
// Increment major number for new feature additions and behavioral changes.
|
||||
// Increment minor number for bug fixes and performance enhancements.
|
||||
version = "v3.6"
|
||||
version = "v3.9"
|
||||
|
||||
// metadata is extra build time data
|
||||
metadata = ""
|
||||
|
||||
61
vendor/helm.sh/helm/v3/pkg/action/action.go
vendored
61
vendor/helm.sh/helm/v3/pkg/action/action.go
vendored
@@ -32,12 +32,12 @@ import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/engine"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/postrender"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
"helm.sh/helm/v3/pkg/storage"
|
||||
@@ -102,11 +102,11 @@ type Configuration struct {
|
||||
// TODO: This function is badly in need of a refactor.
|
||||
// TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed
|
||||
// This code has to do with writing files to disk.
|
||||
func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) {
|
||||
func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) {
|
||||
hs := []*release.Hook{}
|
||||
b := bytes.NewBuffer(nil)
|
||||
|
||||
caps, err := c.getCapabilities()
|
||||
caps, err := cfg.getCapabilities()
|
||||
if err != nil {
|
||||
return hs, b, "", err
|
||||
}
|
||||
@@ -125,12 +125,12 @@ func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values
|
||||
// is mocked. It is not up to the template author to decide when the user wants to
|
||||
// connect to the cluster. So when the user says to dry run, respect the user's
|
||||
// wishes and do not connect to the cluster.
|
||||
if !dryRun && c.RESTClientGetter != nil {
|
||||
rest, err := c.RESTClientGetter.ToRESTConfig()
|
||||
if !dryRun && cfg.RESTClientGetter != nil {
|
||||
restConfig, err := cfg.RESTClientGetter.ToRESTConfig()
|
||||
if err != nil {
|
||||
return hs, b, "", err
|
||||
}
|
||||
files, err2 = engine.RenderWithClient(ch, values, rest)
|
||||
files, err2 = engine.RenderWithClient(ch, values, restConfig)
|
||||
} else {
|
||||
files, err2 = engine.Render(ch, values)
|
||||
}
|
||||
@@ -236,11 +236,11 @@ type RESTClientGetter interface {
|
||||
type DebugLog func(format string, v ...interface{})
|
||||
|
||||
// capabilities builds a Capabilities from discovery information.
|
||||
func (c *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
|
||||
if c.Capabilities != nil {
|
||||
return c.Capabilities, nil
|
||||
func (cfg *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
|
||||
if cfg.Capabilities != nil {
|
||||
return cfg.Capabilities, nil
|
||||
}
|
||||
dc, err := c.RESTClientGetter.ToDiscoveryClient()
|
||||
dc, err := cfg.RESTClientGetter.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get Kubernetes discovery client")
|
||||
}
|
||||
@@ -258,27 +258,28 @@ func (c *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
|
||||
apiVersions, err := GetVersionSet(dc)
|
||||
if err != nil {
|
||||
if discovery.IsGroupDiscoveryFailedError(err) {
|
||||
c.Log("WARNING: The Kubernetes server has an orphaned API service. Server reports: %s", err)
|
||||
c.Log("WARNING: To fix this, kubectl delete apiservice <service-name>")
|
||||
cfg.Log("WARNING: The Kubernetes server has an orphaned API service. Server reports: %s", err)
|
||||
cfg.Log("WARNING: To fix this, kubectl delete apiservice <service-name>")
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "could not get apiVersions from Kubernetes")
|
||||
}
|
||||
}
|
||||
|
||||
c.Capabilities = &chartutil.Capabilities{
|
||||
cfg.Capabilities = &chartutil.Capabilities{
|
||||
APIVersions: apiVersions,
|
||||
KubeVersion: chartutil.KubeVersion{
|
||||
Version: kubeVersion.GitVersion,
|
||||
Major: kubeVersion.Major,
|
||||
Minor: kubeVersion.Minor,
|
||||
},
|
||||
HelmVersion: chartutil.DefaultCapabilities.HelmVersion,
|
||||
}
|
||||
return c.Capabilities, nil
|
||||
return cfg.Capabilities, nil
|
||||
}
|
||||
|
||||
// KubernetesClientSet creates a new kubernetes ClientSet based on the configuration
|
||||
func (c *Configuration) KubernetesClientSet() (kubernetes.Interface, error) {
|
||||
conf, err := c.RESTClientGetter.ToRESTConfig()
|
||||
func (cfg *Configuration) KubernetesClientSet() (kubernetes.Interface, error) {
|
||||
conf, err := cfg.RESTClientGetter.ToRESTConfig()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to generate config for kubernetes client")
|
||||
}
|
||||
@@ -290,20 +291,20 @@ func (c *Configuration) KubernetesClientSet() (kubernetes.Interface, error) {
|
||||
//
|
||||
// If the configuration has a Timestamper on it, that will be used.
|
||||
// Otherwise, this will use time.Now().
|
||||
func (c *Configuration) Now() time.Time {
|
||||
func (cfg *Configuration) Now() time.Time {
|
||||
return Timestamper()
|
||||
}
|
||||
|
||||
func (c *Configuration) releaseContent(name string, version int) (*release.Release, error) {
|
||||
func (cfg *Configuration) releaseContent(name string, version int) (*release.Release, error) {
|
||||
if err := chartutil.ValidateReleaseName(name); err != nil {
|
||||
return nil, errors.Errorf("releaseContent: Release name is invalid: %s", name)
|
||||
}
|
||||
|
||||
if version <= 0 {
|
||||
return c.Releases.Last(name)
|
||||
return cfg.Releases.Last(name)
|
||||
}
|
||||
|
||||
return c.Releases.Get(name, version)
|
||||
return cfg.Releases.Get(name, version)
|
||||
}
|
||||
|
||||
// GetVersionSet retrieves a set of available k8s API versions
|
||||
@@ -355,14 +356,14 @@ func GetVersionSet(client discovery.ServerResourcesInterface) (chartutil.Version
|
||||
}
|
||||
|
||||
// recordRelease with an update operation in case reuse has been set.
|
||||
func (c *Configuration) recordRelease(r *release.Release) {
|
||||
if err := c.Releases.Update(r); err != nil {
|
||||
c.Log("warning: Failed to update release %s: %s", r.Name, err)
|
||||
func (cfg *Configuration) recordRelease(r *release.Release) {
|
||||
if err := cfg.Releases.Update(r); err != nil {
|
||||
cfg.Log("warning: Failed to update release %s: %s", r.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes the action configuration
|
||||
func (c *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace, helmDriver string, log DebugLog) error {
|
||||
func (cfg *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace, helmDriver string, log DebugLog) error {
|
||||
kc := kube.New(getter)
|
||||
kc.Log = log
|
||||
|
||||
@@ -383,8 +384,8 @@ func (c *Configuration) Init(getter genericclioptions.RESTClientGetter, namespac
|
||||
store = storage.Init(d)
|
||||
case "memory":
|
||||
var d *driver.Memory
|
||||
if c.Releases != nil {
|
||||
if mem, ok := c.Releases.Driver.(*driver.Memory); ok {
|
||||
if cfg.Releases != nil {
|
||||
if mem, ok := cfg.Releases.Driver.(*driver.Memory); ok {
|
||||
// This function can be called more than once (e.g., helm list --all-namespaces).
|
||||
// If a memory driver was already initialized, re-use it but set the possibly new namespace.
|
||||
// We re-use it in case some releases where already created in the existing memory driver.
|
||||
@@ -411,10 +412,10 @@ func (c *Configuration) Init(getter genericclioptions.RESTClientGetter, namespac
|
||||
panic("Unknown driver in HELM_DRIVER: " + helmDriver)
|
||||
}
|
||||
|
||||
c.RESTClientGetter = getter
|
||||
c.KubeClient = kc
|
||||
c.Releases = store
|
||||
c.Log = log
|
||||
cfg.RESTClientGetter = getter
|
||||
cfg.KubeClient = kc
|
||||
cfg.Releases = store
|
||||
cfg.Log = log
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
)
|
||||
|
||||
// ChartExport performs a chart export operation.
|
||||
type ChartExport struct {
|
||||
cfg *Configuration
|
||||
|
||||
Destination string
|
||||
}
|
||||
|
||||
// NewChartExport creates a new ChartExport object with the given configuration.
|
||||
func NewChartExport(cfg *Configuration) *ChartExport {
|
||||
return &ChartExport{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart export operation
|
||||
func (a *ChartExport) Run(out io.Writer, ref string) error {
|
||||
r, err := registry.ParseReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch, err := a.cfg.RegistryClient.LoadChart(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save the chart to local destination directory
|
||||
err = chartutil.SaveDir(ch, a.Destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d := filepath.Join(a.Destination, ch.Metadata.Name)
|
||||
fmt.Fprintf(out, "Exported chart to %s/\n", d)
|
||||
return nil
|
||||
}
|
||||
38
vendor/helm.sh/helm/v3/pkg/action/chart_list.go
vendored
38
vendor/helm.sh/helm/v3/pkg/action/chart_list.go
vendored
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// ChartList performs a chart list operation.
|
||||
type ChartList struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewChartList creates a new ChartList object with the given configuration.
|
||||
func NewChartList(cfg *Configuration) *ChartList {
|
||||
return &ChartList{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart list operation
|
||||
func (a *ChartList) Run(out io.Writer) error {
|
||||
return a.cfg.RegistryClient.PrintChartTable()
|
||||
}
|
||||
44
vendor/helm.sh/helm/v3/pkg/action/chart_pull.go
vendored
44
vendor/helm.sh/helm/v3/pkg/action/chart_pull.go
vendored
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
)
|
||||
|
||||
// ChartPull performs a chart pull operation.
|
||||
type ChartPull struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewChartPull creates a new ChartPull object with the given configuration.
|
||||
func NewChartPull(cfg *Configuration) *ChartPull {
|
||||
return &ChartPull{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart pull operation
|
||||
func (a *ChartPull) Run(out io.Writer, ref string) error {
|
||||
r, err := registry.ParseReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.cfg.RegistryClient.PullChartToCache(r)
|
||||
}
|
||||
44
vendor/helm.sh/helm/v3/pkg/action/chart_push.go
vendored
44
vendor/helm.sh/helm/v3/pkg/action/chart_push.go
vendored
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
)
|
||||
|
||||
// ChartPush performs a chart push operation.
|
||||
type ChartPush struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewChartPush creates a new ChartPush object with the given configuration.
|
||||
func NewChartPush(cfg *Configuration) *ChartPush {
|
||||
return &ChartPush{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart push operation
|
||||
func (a *ChartPush) Run(out io.Writer, ref string) error {
|
||||
r, err := registry.ParseReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.cfg.RegistryClient.PushChart(r)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
)
|
||||
|
||||
// ChartRemove performs a chart remove operation.
|
||||
type ChartRemove struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewChartRemove creates a new ChartRemove object with the given configuration.
|
||||
func NewChartRemove(cfg *Configuration) *ChartRemove {
|
||||
return &ChartRemove{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart remove operation
|
||||
func (a *ChartRemove) Run(out io.Writer, ref string) error {
|
||||
r, err := registry.ParseReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.cfg.RegistryClient.RemoveChart(r)
|
||||
}
|
||||
51
vendor/helm.sh/helm/v3/pkg/action/chart_save.go
vendored
51
vendor/helm.sh/helm/v3/pkg/action/chart_save.go
vendored
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
)
|
||||
|
||||
// ChartSave performs a chart save operation.
|
||||
type ChartSave struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewChartSave creates a new ChartSave object with the given configuration.
|
||||
func NewChartSave(cfg *Configuration) *ChartSave {
|
||||
return &ChartSave{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart save operation
|
||||
func (a *ChartSave) Run(out io.Writer, ch *chart.Chart, ref string) error {
|
||||
r, err := registry.ParseReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If no tag is present, use the chart version
|
||||
if r.Tag == "" {
|
||||
r.Tag = ch.Metadata.Version
|
||||
}
|
||||
|
||||
return a.cfg.RegistryClient.SaveChart(ch, r)
|
||||
}
|
||||
@@ -37,11 +37,14 @@ type Dependency struct {
|
||||
Verify bool
|
||||
Keyring string
|
||||
SkipRefresh bool
|
||||
ColumnWidth uint
|
||||
}
|
||||
|
||||
// NewDependency creates a new Dependency object with the given configuration.
|
||||
func NewDependency() *Dependency {
|
||||
return &Dependency{}
|
||||
return &Dependency{
|
||||
ColumnWidth: 80,
|
||||
}
|
||||
}
|
||||
|
||||
// List executes 'helm dependency list'.
|
||||
@@ -181,7 +184,7 @@ func statArchiveForStatus(archive string, dep *chart.Dependency) string {
|
||||
// printDependencies prints all of the dependencies in the yaml file.
|
||||
func (d *Dependency) printDependencies(chartpath string, out io.Writer, c *chart.Chart) {
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = 80
|
||||
table.MaxColWidth = d.ColumnWidth
|
||||
table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS")
|
||||
for _, row := range c.Metadata.Dependencies {
|
||||
table.AddRow(row.Name, row.Version, row.Repository, d.dependencyStatus(chartpath, row, c))
|
||||
@@ -190,7 +193,7 @@ func (d *Dependency) printDependencies(chartpath string, out io.Writer, c *chart
|
||||
}
|
||||
|
||||
// printMissing prints warnings about charts that are present on disk, but are
|
||||
// not in Charts.yaml.
|
||||
// not in Chart.yaml.
|
||||
func (d *Dependency) printMissing(chartpath string, out io.Writer, reqs []*chart.Dependency) {
|
||||
folder := filepath.Join(chartpath, "charts/*")
|
||||
files, err := filepath.Glob(folder)
|
||||
|
||||
106
vendor/helm.sh/helm/v3/pkg/action/install.go
vendored
106
vendor/helm.sh/helm/v3/pkg/action/install.go
vendored
@@ -18,6 +18,7 @@ package action
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
@@ -25,6 +26,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
@@ -44,6 +46,7 @@ import (
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
kubefake "helm.sh/helm/v3/pkg/kube/fake"
|
||||
"helm.sh/helm/v3/pkg/postrender"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
@@ -51,13 +54,6 @@ import (
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
)
|
||||
|
||||
// releaseNameMaxLen is the maximum length of a release name.
|
||||
//
|
||||
// As of Kubernetes 1.4, the max limit on a name is 63 chars. We reserve 10 for
|
||||
// charts to add data. Effectively, that gives us 53 chars.
|
||||
// See https://github.com/helm/helm/issues/1528
|
||||
const releaseNameMaxLen = 53
|
||||
|
||||
// NOTESFILE_SUFFIX that we want to treat special. It goes through the templating engine
|
||||
// but it's not a yaml file (resource) hence can't have hooks, etc. And the user actually
|
||||
// wants to see this file after rendering in the status command. However, it must be a suffix
|
||||
@@ -104,6 +100,8 @@ type Install struct {
|
||||
// OutputDir/<ReleaseName>
|
||||
UseReleaseName bool
|
||||
PostRenderer postrender.PostRenderer
|
||||
// Lock to control raceconditions when the process receives a SIGTERM
|
||||
Lock sync.Mutex
|
||||
}
|
||||
|
||||
// ChartPathOptions captures common options used for controlling chart paths
|
||||
@@ -119,13 +117,20 @@ type ChartPathOptions struct {
|
||||
Username string // --username
|
||||
Verify bool // --verify
|
||||
Version string // --version
|
||||
|
||||
// registryClient provides a registry client but is not added with
|
||||
// options from a flag
|
||||
registryClient *registry.Client
|
||||
}
|
||||
|
||||
// NewInstall creates a new Install object with the given configuration.
|
||||
func NewInstall(cfg *Configuration) *Install {
|
||||
return &Install{
|
||||
in := &Install{
|
||||
cfg: cfg,
|
||||
}
|
||||
in.ChartPathOptions.registryClient = cfg.RegistryClient
|
||||
|
||||
return in
|
||||
}
|
||||
|
||||
func (i *Install) installCRDs(crds []chart.CRD) error {
|
||||
@@ -174,7 +179,14 @@ func (i *Install) installCRDs(crds []chart.CRD) error {
|
||||
// Run executes the installation
|
||||
//
|
||||
// If DryRun is set to true, this will prepare the release, but not install it
|
||||
|
||||
func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.Release, error) {
|
||||
ctx := context.Background()
|
||||
return i.RunWithContext(ctx, chrt, vals)
|
||||
}
|
||||
|
||||
// Run executes the installation with Context
|
||||
func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals map[string]interface{}) (*release.Release, error) {
|
||||
// Check reachability of cluster unless in client-only mode (e.g. `helm template` without `--validate`)
|
||||
if !i.ClientOnly {
|
||||
if err := i.cfg.KubeClient.IsReachable(); err != nil {
|
||||
@@ -186,6 +198,10 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := chartutil.ProcessDependencies(chrt, vals); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Pre-install anything in the crd/ directory. We do this before Helm
|
||||
// contacts the upstream server and builds the capabilities object.
|
||||
if crds := chrt.CRDObjects(); !i.ClientOnly && !i.SkipCRDs && len(crds) > 0 {
|
||||
@@ -214,10 +230,6 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
|
||||
i.cfg.Log("API Version list given outside of client only mode, this list will be ignored")
|
||||
}
|
||||
|
||||
if err := chartutil.ProcessDependencies(chrt, vals); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure if Atomic is set, that wait is set as well. This makes it so
|
||||
// the user doesn't have to specify both
|
||||
i.Wait = i.Wait || i.Atomic
|
||||
@@ -227,7 +239,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//special case for helm template --is-upgrade
|
||||
// special case for helm template --is-upgrade
|
||||
isUpgrade := i.IsUpgrade && i.DryRun
|
||||
options := chartutil.ReleaseOptions{
|
||||
Name: i.ReleaseName,
|
||||
@@ -331,11 +343,23 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
|
||||
// not working.
|
||||
return rel, err
|
||||
}
|
||||
rChan := make(chan resultMessage)
|
||||
doneChan := make(chan struct{})
|
||||
defer close(doneChan)
|
||||
go i.performInstall(rChan, rel, toBeAdopted, resources)
|
||||
go i.handleContext(ctx, rChan, doneChan, rel)
|
||||
result := <-rChan
|
||||
//start preformInstall go routine
|
||||
return result.r, result.e
|
||||
}
|
||||
|
||||
func (i *Install) performInstall(c chan<- resultMessage, rel *release.Release, toBeAdopted kube.ResourceList, resources kube.ResourceList) {
|
||||
|
||||
// pre-install hooks
|
||||
if !i.DisableHooks {
|
||||
if err := i.cfg.execHook(rel, release.HookPreInstall, i.Timeout); err != nil {
|
||||
return i.failRelease(rel, fmt.Errorf("failed pre-install: %s", err))
|
||||
i.reportToRun(c, rel, fmt.Errorf("failed pre-install: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,29 +368,34 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
|
||||
// to true, since that is basically an upgrade operation.
|
||||
if len(toBeAdopted) == 0 && len(resources) > 0 {
|
||||
if _, err := i.cfg.KubeClient.Create(resources); err != nil {
|
||||
return i.failRelease(rel, err)
|
||||
i.reportToRun(c, rel, err)
|
||||
return
|
||||
}
|
||||
} else if len(resources) > 0 {
|
||||
if _, err := i.cfg.KubeClient.Update(toBeAdopted, resources, false); err != nil {
|
||||
return i.failRelease(rel, err)
|
||||
i.reportToRun(c, rel, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if i.Wait {
|
||||
if i.WaitForJobs {
|
||||
if err := i.cfg.KubeClient.WaitWithJobs(resources, i.Timeout); err != nil {
|
||||
return i.failRelease(rel, err)
|
||||
i.reportToRun(c, rel, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := i.cfg.KubeClient.Wait(resources, i.Timeout); err != nil {
|
||||
return i.failRelease(rel, err)
|
||||
i.reportToRun(c, rel, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !i.DisableHooks {
|
||||
if err := i.cfg.execHook(rel, release.HookPostInstall, i.Timeout); err != nil {
|
||||
return i.failRelease(rel, fmt.Errorf("failed post-install: %s", err))
|
||||
i.reportToRun(c, rel, fmt.Errorf("failed post-install: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,9 +416,25 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
|
||||
i.cfg.Log("failed to record the release: %s", err)
|
||||
}
|
||||
|
||||
return rel, nil
|
||||
i.reportToRun(c, rel, nil)
|
||||
}
|
||||
func (i *Install) handleContext(ctx context.Context, c chan<- resultMessage, done chan struct{}, rel *release.Release) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err := ctx.Err()
|
||||
i.reportToRun(c, rel, err)
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
func (i *Install) reportToRun(c chan<- resultMessage, rel *release.Release, err error) {
|
||||
i.Lock.Lock()
|
||||
if err != nil {
|
||||
rel, err = i.failRelease(rel, err)
|
||||
}
|
||||
c <- resultMessage{r: rel, e: err}
|
||||
i.Lock.Unlock()
|
||||
}
|
||||
|
||||
func (i *Install) failRelease(rel *release.Release, err error) (*release.Release, error) {
|
||||
rel.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", i.ReleaseName, err.Error()))
|
||||
if i.Atomic {
|
||||
@@ -417,14 +462,10 @@ func (i *Install) failRelease(rel *release.Release, err error) (*release.Release
|
||||
// - used by a deleted release, and i.Replace is false
|
||||
func (i *Install) availableName() error {
|
||||
start := i.ReleaseName
|
||||
if start == "" {
|
||||
return errors.New("name is required")
|
||||
}
|
||||
|
||||
if len(start) > releaseNameMaxLen {
|
||||
return errors.Errorf("release name %q exceeds max length of %d", start, releaseNameMaxLen)
|
||||
if err := chartutil.ValidateReleaseName(start); err != nil {
|
||||
return errors.Wrapf(err, "release name %q", start)
|
||||
}
|
||||
|
||||
if i.DryRun {
|
||||
return nil
|
||||
}
|
||||
@@ -632,6 +673,12 @@ OUTER:
|
||||
//
|
||||
// If 'verify' was set on ChartPathOptions, this will attempt to also verify the chart.
|
||||
func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (string, error) {
|
||||
// If there is no registry client and the name is in an OCI registry return
|
||||
// an error and a lookup will not occur.
|
||||
if registry.IsOCI(name) && c.registryClient == nil {
|
||||
return "", fmt.Errorf("unable to lookup chart %q, missing registry client", name)
|
||||
}
|
||||
|
||||
name = strings.TrimSpace(name)
|
||||
version := strings.TrimSpace(c.Version)
|
||||
|
||||
@@ -662,7 +709,9 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
|
||||
},
|
||||
RepositoryConfig: settings.RepositoryConfig,
|
||||
RepositoryCache: settings.RepositoryCache,
|
||||
RegistryClient: c.registryClient,
|
||||
}
|
||||
|
||||
if c.Verify {
|
||||
dl.Verify = downloader.VerifyAlways
|
||||
}
|
||||
@@ -716,5 +765,6 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
|
||||
if version != "" {
|
||||
atVersion = fmt.Sprintf(" at version %q", version)
|
||||
}
|
||||
return filename, errors.Errorf("failed to download %q%s (hint: running `helm repo update` may help)", name, atVersion)
|
||||
|
||||
return filename, errors.Errorf("failed to download %q%s", name, atVersion)
|
||||
}
|
||||
|
||||
13
vendor/helm.sh/helm/v3/pkg/action/lint.go
vendored
13
vendor/helm.sh/helm/v3/pkg/action/lint.go
vendored
@@ -36,6 +36,7 @@ type Lint struct {
|
||||
Strict bool
|
||||
Namespace string
|
||||
WithSubcharts bool
|
||||
Quiet bool
|
||||
}
|
||||
|
||||
// LintResult is the result of Lint
|
||||
@@ -75,6 +76,16 @@ func (l *Lint) Run(paths []string, vals map[string]interface{}) *LintResult {
|
||||
return result
|
||||
}
|
||||
|
||||
// HasWaringsOrErrors checks is LintResult has any warnings or errors
|
||||
func HasWarningsOrErrors(result *LintResult) bool {
|
||||
for _, msg := range result.Messages {
|
||||
if msg.Severity > support.InfoSev {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func lintChart(path string, vals map[string]interface{}, namespace string, strict bool) (support.Linter, error) {
|
||||
var chartPath string
|
||||
linter := support.Linter{}
|
||||
@@ -96,7 +107,7 @@ func lintChart(path string, vals map[string]interface{}, namespace string, stric
|
||||
return linter, errors.Wrap(err, "unable to extract tarball")
|
||||
}
|
||||
|
||||
files, err := ioutil.ReadDir(tempDir)
|
||||
files, err := os.ReadDir(tempDir)
|
||||
if err != nil {
|
||||
return linter, errors.Wrapf(err, "unable to read temporary output directory %s", tempDir)
|
||||
}
|
||||
|
||||
11
vendor/helm.sh/helm/v3/pkg/action/pull.go
vendored
11
vendor/helm.sh/helm/v3/pkg/action/pull.go
vendored
@@ -29,6 +29,7 @@ import (
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/downloader"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
@@ -86,18 +87,14 @@ func (p *Pull) Run(chartRef string) (string, error) {
|
||||
getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile),
|
||||
getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify),
|
||||
},
|
||||
RegistryClient: p.cfg.RegistryClient,
|
||||
RepositoryConfig: p.Settings.RepositoryConfig,
|
||||
RepositoryCache: p.Settings.RepositoryCache,
|
||||
}
|
||||
|
||||
if strings.HasPrefix(chartRef, "oci://") {
|
||||
if p.Version == "" {
|
||||
return out.String(), errors.Errorf("--version flag is explicitly required for OCI registries")
|
||||
}
|
||||
|
||||
if registry.IsOCI(chartRef) {
|
||||
c.Options = append(c.Options,
|
||||
getter.WithRegistryClient(p.cfg.RegistryClient),
|
||||
getter.WithTagName(p.Version))
|
||||
getter.WithRegistryClient(p.cfg.RegistryClient))
|
||||
}
|
||||
|
||||
if p.Verify {
|
||||
|
||||
70
vendor/helm.sh/helm/v3/pkg/action/push.go
vendored
Normal file
70
vendor/helm.sh/helm/v3/pkg/action/push.go
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/pusher"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/uploader"
|
||||
)
|
||||
|
||||
// Push is the action for uploading a chart.
|
||||
//
|
||||
// It provides the implementation of 'helm push'.
|
||||
type Push struct {
|
||||
Settings *cli.EnvSettings
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// PushOpt is a type of function that sets options for a push action.
|
||||
type PushOpt func(*Push)
|
||||
|
||||
// WithPushConfig sets the cfg field on the push configuration object.
|
||||
func WithPushConfig(cfg *Configuration) PushOpt {
|
||||
return func(p *Push) {
|
||||
p.cfg = cfg
|
||||
}
|
||||
}
|
||||
|
||||
// NewPushWithOpts creates a new push, with configuration options.
|
||||
func NewPushWithOpts(opts ...PushOpt) *Push {
|
||||
p := &Push{}
|
||||
for _, fn := range opts {
|
||||
fn(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Run executes 'helm push' against the given chart archive.
|
||||
func (p *Push) Run(chartRef string, remote string) (string, error) {
|
||||
var out strings.Builder
|
||||
|
||||
c := uploader.ChartUploader{
|
||||
Out: &out,
|
||||
Pushers: pusher.All(p.Settings),
|
||||
Options: []pusher.Option{},
|
||||
}
|
||||
|
||||
if registry.IsOCI(remote) {
|
||||
c.Options = append(c.Options, pusher.WithRegistryClient(p.cfg.RegistryClient))
|
||||
}
|
||||
|
||||
return out.String(), c.UploadTo(chartRef, remote)
|
||||
}
|
||||
@@ -18,6 +18,8 @@ package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
)
|
||||
|
||||
// RegistryLogin performs a registry login operation.
|
||||
@@ -34,5 +36,8 @@ func NewRegistryLogin(cfg *Configuration) *RegistryLogin {
|
||||
|
||||
// Run executes the registry login operation
|
||||
func (a *RegistryLogin) Run(out io.Writer, hostname string, username string, password string, insecure bool) error {
|
||||
return a.cfg.RegistryClient.Login(hostname, username, password, insecure)
|
||||
return a.cfg.RegistryClient.Login(
|
||||
hostname,
|
||||
registry.LoginOptBasicAuth(username, password),
|
||||
registry.LoginOptInsecure(insecure))
|
||||
}
|
||||
|
||||
@@ -164,6 +164,11 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
|
||||
r.cfg.Log("rollback hooks disabled for %s", targetRelease.Name)
|
||||
}
|
||||
|
||||
// It is safe to use "force" here because these are resources currently rendered by the chart.
|
||||
err = target.Visit(setMetadataVisitor(targetRelease.Name, targetRelease.Namespace, true))
|
||||
if err != nil {
|
||||
return targetRelease, errors.Wrap(err, "unable to set metadata visitor from target release")
|
||||
}
|
||||
results, err := r.cfg.KubeClient.Update(current, target, r.Force)
|
||||
|
||||
if err != nil {
|
||||
|
||||
38
vendor/helm.sh/helm/v3/pkg/action/show.go
vendored
38
vendor/helm.sh/helm/v3/pkg/action/show.go
vendored
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package action
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -41,6 +42,8 @@ const (
|
||||
ShowValues ShowOutputFormat = "values"
|
||||
// ShowReadme is the format which only shows the chart's README
|
||||
ShowReadme ShowOutputFormat = "readme"
|
||||
// ShowCRDs is the format which only shows the chart's CRDs
|
||||
ShowCRDs ShowOutputFormat = "crds"
|
||||
)
|
||||
|
||||
var readmeFileNames = []string{"readme.md", "readme.txt", "readme"}
|
||||
@@ -61,12 +64,24 @@ type Show struct {
|
||||
}
|
||||
|
||||
// NewShow creates a new Show object with the given configuration.
|
||||
// Deprecated: Use NewShowWithConfig
|
||||
// TODO Helm 4: Fold NewShowWithConfig back into NewShow
|
||||
func NewShow(output ShowOutputFormat) *Show {
|
||||
return &Show{
|
||||
OutputFormat: output,
|
||||
}
|
||||
}
|
||||
|
||||
// NewShowWithConfig creates a new Show object with the given configuration.
|
||||
func NewShowWithConfig(output ShowOutputFormat, cfg *Configuration) *Show {
|
||||
sh := &Show{
|
||||
OutputFormat: output,
|
||||
}
|
||||
sh.ChartPathOptions.registryClient = cfg.RegistryClient
|
||||
|
||||
return sh
|
||||
}
|
||||
|
||||
// Run executes 'helm show' against the given release.
|
||||
func (s *Show) Run(chartpath string) (string, error) {
|
||||
if s.chart == nil {
|
||||
@@ -106,14 +121,25 @@ func (s *Show) Run(chartpath string) (string, error) {
|
||||
}
|
||||
|
||||
if s.OutputFormat == ShowReadme || s.OutputFormat == ShowAll {
|
||||
if s.OutputFormat == ShowAll {
|
||||
fmt.Fprintln(&out, "---")
|
||||
}
|
||||
readme := findReadme(s.chart.Files)
|
||||
if readme == nil {
|
||||
return out.String(), nil
|
||||
if readme != nil {
|
||||
if s.OutputFormat == ShowAll {
|
||||
fmt.Fprintln(&out, "---")
|
||||
}
|
||||
fmt.Fprintf(&out, "%s\n", readme.Data)
|
||||
}
|
||||
}
|
||||
|
||||
if s.OutputFormat == ShowCRDs || s.OutputFormat == ShowAll {
|
||||
crds := s.chart.CRDObjects()
|
||||
if len(crds) > 0 {
|
||||
if s.OutputFormat == ShowAll && !bytes.HasPrefix(crds[0].File.Data, []byte("---")) {
|
||||
fmt.Fprintln(&out, "---")
|
||||
}
|
||||
for _, crd := range crds {
|
||||
fmt.Fprintf(&out, "%s\n", string(crd.File.Data))
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&out, "%s\n", readme.Data)
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
24
vendor/helm.sh/helm/v3/pkg/action/uninstall.go
vendored
24
vendor/helm.sh/helm/v3/pkg/action/uninstall.go
vendored
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
@@ -37,6 +38,7 @@ type Uninstall struct {
|
||||
DisableHooks bool
|
||||
DryRun bool
|
||||
KeepHistory bool
|
||||
Wait bool
|
||||
Timeout time.Duration
|
||||
Description string
|
||||
}
|
||||
@@ -110,13 +112,21 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
|
||||
u.cfg.Log("uninstall: Failed to store updated release: %s", err)
|
||||
}
|
||||
|
||||
kept, errs := u.deleteRelease(rel)
|
||||
deletedResources, kept, errs := u.deleteRelease(rel)
|
||||
|
||||
if kept != "" {
|
||||
kept = "These resources were kept due to the resource policy:\n" + kept
|
||||
}
|
||||
res.Info = kept
|
||||
|
||||
if u.Wait {
|
||||
if kubeClient, ok := u.cfg.KubeClient.(kube.InterfaceExt); ok {
|
||||
if err := kubeClient.WaitForDelete(deletedResources, u.Timeout); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !u.DisableHooks {
|
||||
if err := u.cfg.execHook(rel, release.HookPostDelete, u.Timeout); err != nil {
|
||||
errs = append(errs, err)
|
||||
@@ -172,12 +182,12 @@ func joinErrors(errs []error) string {
|
||||
return strings.Join(es, "; ")
|
||||
}
|
||||
|
||||
// deleteRelease deletes the release and returns manifests that were kept in the deletion process
|
||||
func (u *Uninstall) deleteRelease(rel *release.Release) (string, []error) {
|
||||
// deleteRelease deletes the release and returns list of delete resources and manifests that were kept in the deletion process
|
||||
func (u *Uninstall) deleteRelease(rel *release.Release) (kube.ResourceList, string, []error) {
|
||||
var errs []error
|
||||
caps, err := u.cfg.getCapabilities()
|
||||
if err != nil {
|
||||
return rel.Manifest, []error{errors.Wrap(err, "could not get apiVersions from Kubernetes")}
|
||||
return nil, rel.Manifest, []error{errors.Wrap(err, "could not get apiVersions from Kubernetes")}
|
||||
}
|
||||
|
||||
manifests := releaseutil.SplitManifests(rel.Manifest)
|
||||
@@ -187,7 +197,7 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (string, []error) {
|
||||
// FIXME: One way to delete at this point would be to try a label-based
|
||||
// deletion. The problem with this is that we could get a false positive
|
||||
// and delete something that was not legitimately part of this release.
|
||||
return rel.Manifest, []error{errors.Wrap(err, "corrupted release record. You must manually delete the resources")}
|
||||
return nil, rel.Manifest, []error{errors.Wrap(err, "corrupted release record. You must manually delete the resources")}
|
||||
}
|
||||
|
||||
filesToKeep, filesToDelete := filterManifestsToKeep(files)
|
||||
@@ -203,10 +213,10 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (string, []error) {
|
||||
|
||||
resources, err := u.cfg.KubeClient.Build(strings.NewReader(builder.String()), false)
|
||||
if err != nil {
|
||||
return "", []error{errors.Wrap(err, "unable to build kubernetes objects for delete")}
|
||||
return nil, "", []error{errors.Wrap(err, "unable to build kubernetes objects for delete")}
|
||||
}
|
||||
if len(resources) > 0 {
|
||||
_, errs = u.cfg.KubeClient.Delete(resources)
|
||||
}
|
||||
return kept, errs
|
||||
return resources, kept, errs
|
||||
}
|
||||
|
||||
82
vendor/helm.sh/helm/v3/pkg/action/upgrade.go
vendored
82
vendor/helm.sh/helm/v3/pkg/action/upgrade.go
vendored
@@ -21,6 +21,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -98,17 +99,35 @@ type Upgrade struct {
|
||||
PostRenderer postrender.PostRenderer
|
||||
// DisableOpenAPIValidation controls whether OpenAPI validation is enforced.
|
||||
DisableOpenAPIValidation bool
|
||||
// Get missing dependencies
|
||||
DependencyUpdate bool
|
||||
// Lock to control raceconditions when the process receives a SIGTERM
|
||||
Lock sync.Mutex
|
||||
}
|
||||
|
||||
type resultMessage struct {
|
||||
r *release.Release
|
||||
e error
|
||||
}
|
||||
|
||||
// NewUpgrade creates a new Upgrade object with the given configuration.
|
||||
func NewUpgrade(cfg *Configuration) *Upgrade {
|
||||
return &Upgrade{
|
||||
up := &Upgrade{
|
||||
cfg: cfg,
|
||||
}
|
||||
up.ChartPathOptions.registryClient = cfg.RegistryClient
|
||||
|
||||
return up
|
||||
}
|
||||
|
||||
// Run executes the upgrade on the given release.
|
||||
func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, error) {
|
||||
ctx := context.Background()
|
||||
return u.RunWithContext(ctx, name, chart, vals)
|
||||
}
|
||||
|
||||
// RunWithContext executes the upgrade on the given release with context.
|
||||
func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, error) {
|
||||
if err := u.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -129,7 +148,7 @@ func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface
|
||||
u.cfg.Releases.MaxHistory = u.MaxHistory
|
||||
|
||||
u.cfg.Log("performing update for %s", name)
|
||||
res, err := u.performUpgrade(currentRelease, upgradedRelease)
|
||||
res, err := u.performUpgrade(ctx, currentRelease, upgradedRelease)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -241,7 +260,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
|
||||
return currentRelease, upgradedRelease, err
|
||||
}
|
||||
|
||||
func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Release) (*release.Release, error) {
|
||||
func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedRelease *release.Release) (*release.Release, error) {
|
||||
current, err := u.cfg.KubeClient.Build(bytes.NewBufferString(originalRelease.Manifest), false)
|
||||
if err != nil {
|
||||
// Checking for removed Kubernetes API error so can provide a more informative error message to the user
|
||||
@@ -304,11 +323,51 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
|
||||
if err := u.cfg.Releases.Create(upgradedRelease); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rChan := make(chan resultMessage)
|
||||
ctxChan := make(chan resultMessage)
|
||||
doneChan := make(chan interface{})
|
||||
defer close(doneChan)
|
||||
go u.releasingUpgrade(rChan, upgradedRelease, current, target, originalRelease)
|
||||
go u.handleContext(ctx, doneChan, ctxChan, upgradedRelease)
|
||||
select {
|
||||
case result := <-rChan:
|
||||
return result.r, result.e
|
||||
case result := <-ctxChan:
|
||||
return result.r, result.e
|
||||
}
|
||||
}
|
||||
|
||||
// Function used to lock the Mutex, this is important for the case when the atomic flag is set.
|
||||
// In that case the upgrade will finish before the rollback is finished so it is necessary to wait for the rollback to finish.
|
||||
// The rollback will be trigger by the function failRelease
|
||||
func (u *Upgrade) reportToPerformUpgrade(c chan<- resultMessage, rel *release.Release, created kube.ResourceList, err error) {
|
||||
u.Lock.Lock()
|
||||
if err != nil {
|
||||
rel, err = u.failRelease(rel, created, err)
|
||||
}
|
||||
c <- resultMessage{r: rel, e: err}
|
||||
u.Lock.Unlock()
|
||||
}
|
||||
|
||||
// Setup listener for SIGINT and SIGTERM
|
||||
func (u *Upgrade) handleContext(ctx context.Context, done chan interface{}, c chan<- resultMessage, upgradedRelease *release.Release) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err := ctx.Err()
|
||||
|
||||
// when the atomic flag is set the ongoing release finish first and doesn't give time for the rollback happens.
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, err)
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *release.Release, current kube.ResourceList, target kube.ResourceList, originalRelease *release.Release) {
|
||||
// pre-upgrade hooks
|
||||
|
||||
if !u.DisableHooks {
|
||||
if err := u.cfg.execHook(upgradedRelease, release.HookPreUpgrade, u.Timeout); err != nil {
|
||||
return u.failRelease(upgradedRelease, kube.ResourceList{}, fmt.Errorf("pre-upgrade hooks failed: %s", err))
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, fmt.Errorf("pre-upgrade hooks failed: %s", err))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
u.cfg.Log("upgrade hooks disabled for %s", upgradedRelease.Name)
|
||||
@@ -317,7 +376,8 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
|
||||
results, err := u.cfg.KubeClient.Update(current, target, u.Force)
|
||||
if err != nil {
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
return u.failRelease(upgradedRelease, results.Created, err)
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
|
||||
return
|
||||
}
|
||||
|
||||
if u.Recreate {
|
||||
@@ -334,12 +394,14 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
|
||||
if u.WaitForJobs {
|
||||
if err := u.cfg.KubeClient.WaitWithJobs(target, u.Timeout); err != nil {
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
return u.failRelease(upgradedRelease, results.Created, err)
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := u.cfg.KubeClient.Wait(target, u.Timeout); err != nil {
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
return u.failRelease(upgradedRelease, results.Created, err)
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,7 +409,8 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
|
||||
// post-upgrade hooks
|
||||
if !u.DisableHooks {
|
||||
if err := u.cfg.execHook(upgradedRelease, release.HookPostUpgrade, u.Timeout); err != nil {
|
||||
return u.failRelease(upgradedRelease, results.Created, fmt.Errorf("post-upgrade hooks failed: %s", err))
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, fmt.Errorf("post-upgrade hooks failed: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,8 +423,7 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
|
||||
} else {
|
||||
upgradedRelease.Info.Description = "Upgrade complete"
|
||||
}
|
||||
|
||||
return upgradedRelease, nil
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, nil, nil)
|
||||
}
|
||||
|
||||
func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, err error) (*release.Release, error) {
|
||||
|
||||
@@ -51,7 +51,7 @@ func existingResourceConflict(resources kube.ResourceList, releaseName, releaseN
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrap(err, "could not get information about the resource")
|
||||
return errors.Wrapf(err, "could not get information about the resource %s", resourceString(info))
|
||||
}
|
||||
|
||||
// Allow adoption of the resource if it is managed by Helm and is annotated with correct release name and namespace.
|
||||
|
||||
@@ -28,21 +28,21 @@ import (
|
||||
helmversion "helm.sh/helm/v3/internal/version"
|
||||
)
|
||||
|
||||
const (
|
||||
k8sVersionMajor = 1
|
||||
k8sVersionMinor = 20
|
||||
)
|
||||
|
||||
var (
|
||||
// The Kubernetes version can be set by LDFLAGS. In order to do that the value
|
||||
// must be a string.
|
||||
k8sVersionMajor = "1"
|
||||
k8sVersionMinor = "20"
|
||||
|
||||
// DefaultVersionSet is the default version set, which includes only Core V1 ("v1").
|
||||
DefaultVersionSet = allKnownVersions()
|
||||
|
||||
// DefaultCapabilities is the default set of capabilities.
|
||||
DefaultCapabilities = &Capabilities{
|
||||
KubeVersion: KubeVersion{
|
||||
Version: fmt.Sprintf("v%d.%d.0", k8sVersionMajor, k8sVersionMinor),
|
||||
Major: strconv.Itoa(k8sVersionMajor),
|
||||
Minor: strconv.Itoa(k8sVersionMinor),
|
||||
Version: fmt.Sprintf("v%s.%s.0", k8sVersionMajor, k8sVersionMinor),
|
||||
Major: k8sVersionMajor,
|
||||
Minor: k8sVersionMinor,
|
||||
},
|
||||
APIVersions: DefaultVersionSet,
|
||||
HelmVersion: helmversion.Get(),
|
||||
|
||||
74
vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go
vendored
74
vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go
vendored
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package chartutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/mitchellh/copystructure"
|
||||
@@ -25,6 +26,13 @@ import (
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
)
|
||||
|
||||
func concatPrefix(a, b string) string {
|
||||
if a == "" {
|
||||
return b
|
||||
}
|
||||
return fmt.Sprintf("%s.%s", a, b)
|
||||
}
|
||||
|
||||
// CoalesceValues coalesces all of the values in a chart (and its subcharts).
|
||||
//
|
||||
// Values are coalesced together using the following rules:
|
||||
@@ -45,19 +53,21 @@ func CoalesceValues(chrt *chart.Chart, vals map[string]interface{}) (Values, err
|
||||
if valsCopy == nil {
|
||||
valsCopy = make(map[string]interface{})
|
||||
}
|
||||
return coalesce(chrt, valsCopy)
|
||||
return coalesce(log.Printf, chrt, valsCopy, "")
|
||||
}
|
||||
|
||||
type printFn func(format string, v ...interface{})
|
||||
|
||||
// coalesce coalesces the dest values and the chart values, giving priority to the dest values.
|
||||
//
|
||||
// This is a helper function for CoalesceValues.
|
||||
func coalesce(ch *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) {
|
||||
coalesceValues(ch, dest)
|
||||
return coalesceDeps(ch, dest)
|
||||
func coalesce(printf printFn, ch *chart.Chart, dest map[string]interface{}, prefix string) (map[string]interface{}, error) {
|
||||
coalesceValues(printf, ch, dest, prefix)
|
||||
return coalesceDeps(printf, ch, dest, prefix)
|
||||
}
|
||||
|
||||
// coalesceDeps coalesces the dependencies of the given chart.
|
||||
func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) {
|
||||
func coalesceDeps(printf printFn, chrt *chart.Chart, dest map[string]interface{}, prefix string) (map[string]interface{}, error) {
|
||||
for _, subchart := range chrt.Dependencies() {
|
||||
if c, ok := dest[subchart.Name()]; !ok {
|
||||
// If dest doesn't already have the key, create it.
|
||||
@@ -67,13 +77,14 @@ func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]in
|
||||
}
|
||||
if dv, ok := dest[subchart.Name()]; ok {
|
||||
dvmap := dv.(map[string]interface{})
|
||||
subPrefix := concatPrefix(prefix, chrt.Metadata.Name)
|
||||
|
||||
// Get globals out of dest and merge them into dvmap.
|
||||
coalesceGlobals(dvmap, dest)
|
||||
coalesceGlobals(printf, dvmap, dest, subPrefix)
|
||||
|
||||
// Now coalesce the rest of the values.
|
||||
var err error
|
||||
dest[subchart.Name()], err = coalesce(subchart, dvmap)
|
||||
dest[subchart.Name()], err = coalesce(printf, subchart, dvmap, subPrefix)
|
||||
if err != nil {
|
||||
return dest, err
|
||||
}
|
||||
@@ -85,20 +96,20 @@ 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{}) {
|
||||
func coalesceGlobals(printf printFn, dest, src map[string]interface{}, prefix string) {
|
||||
var dg, sg map[string]interface{}
|
||||
|
||||
if destglob, ok := dest[GlobalKey]; !ok {
|
||||
dg = make(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)
|
||||
printf("warning: skipping globals because destination %s is not a table.", GlobalKey)
|
||||
return
|
||||
}
|
||||
|
||||
if srcglob, ok := src[GlobalKey]; !ok {
|
||||
sg = make(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)
|
||||
printf("warning: skipping globals because source %s is not a table.", GlobalKey)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -114,22 +125,22 @@ func coalesceGlobals(dest, src map[string]interface{}) {
|
||||
dg[key] = vv
|
||||
} else {
|
||||
if destvmap, ok := destv.(map[string]interface{}); !ok {
|
||||
log.Printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key)
|
||||
printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key)
|
||||
} else {
|
||||
// Basically, we reverse order of coalesce here to merge
|
||||
// top-down.
|
||||
CoalesceTables(vv, destvmap)
|
||||
subPrefix := concatPrefix(prefix, key)
|
||||
coalesceTablesFullKey(printf, vv, destvmap, subPrefix)
|
||||
dg[key] = vv
|
||||
continue
|
||||
}
|
||||
}
|
||||
} 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
|
||||
printf("key %s is table. Skipping", key)
|
||||
} else {
|
||||
// TODO: Do we need to do any additional checking on the value?
|
||||
dg[key] = val
|
||||
}
|
||||
// TODO: Do we need to do any additional checking on the value?
|
||||
dg[key] = val
|
||||
}
|
||||
dest[GlobalKey] = dg
|
||||
}
|
||||
@@ -145,7 +156,8 @@ func copyMap(src map[string]interface{}) map[string]interface{} {
|
||||
// coalesceValues builds up a values map for a particular chart.
|
||||
//
|
||||
// Values in v will override the values in the chart.
|
||||
func coalesceValues(c *chart.Chart, v map[string]interface{}) {
|
||||
func coalesceValues(printf printFn, c *chart.Chart, v map[string]interface{}, prefix string) {
|
||||
subPrefix := concatPrefix(prefix, c.Metadata.Name)
|
||||
for key, val := range c.Values {
|
||||
if value, ok := v[key]; ok {
|
||||
if value == nil {
|
||||
@@ -158,15 +170,15 @@ func coalesceValues(c *chart.Chart, v map[string]interface{}) {
|
||||
src, ok := val.(map[string]interface{})
|
||||
if !ok {
|
||||
// If the original value is nil, there is nothing to coalesce, so we don't print
|
||||
// the warning but simply continue
|
||||
// the warning
|
||||
if val != nil {
|
||||
log.Printf("warning: skipped value for %s: Not a table.", key)
|
||||
printf("warning: skipped value for %s.%s: Not a table.", subPrefix, key)
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
// Because v has higher precedence than nv, dest values override src
|
||||
// values.
|
||||
coalesceTablesFullKey(printf, dest, src, concatPrefix(subPrefix, key))
|
||||
}
|
||||
// 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.
|
||||
@@ -179,6 +191,13 @@ func coalesceValues(c *chart.Chart, v map[string]interface{}) {
|
||||
//
|
||||
// dest is considered authoritative.
|
||||
func CoalesceTables(dst, src map[string]interface{}) map[string]interface{} {
|
||||
return coalesceTablesFullKey(log.Printf, dst, src, "")
|
||||
}
|
||||
|
||||
// coalesceTablesFullKey merges a source map into a destination map.
|
||||
//
|
||||
// dest is considered authoritative.
|
||||
func coalesceTablesFullKey(printf printFn, dst, src map[string]interface{}, prefix string) map[string]interface{} {
|
||||
// When --reuse-values is set but there are no modifications yet, return new values
|
||||
if src == nil {
|
||||
return dst
|
||||
@@ -189,18 +208,19 @@ func CoalesceTables(dst, src map[string]interface{}) map[string]interface{} {
|
||||
// Because dest has higher precedence than src, dest values override src
|
||||
// values.
|
||||
for key, val := range src {
|
||||
fullkey := concatPrefix(prefix, key)
|
||||
if dv, ok := dst[key]; ok && dv == nil {
|
||||
delete(dst, key)
|
||||
} else if !ok {
|
||||
dst[key] = val
|
||||
} else if istable(val) {
|
||||
if istable(dv) {
|
||||
CoalesceTables(dv.(map[string]interface{}), val.(map[string]interface{}))
|
||||
coalesceTablesFullKey(printf, dv.(map[string]interface{}), val.(map[string]interface{}), fullkey)
|
||||
} else {
|
||||
log.Printf("warning: cannot overwrite table with non table for %s (%v)", key, val)
|
||||
printf("warning: cannot overwrite table with non table for %s (%v)", fullkey, val)
|
||||
}
|
||||
} else if istable(dv) && val != nil {
|
||||
log.Printf("warning: destination for %s is a table. Ignoring non-table value %v", key, val)
|
||||
printf("warning: destination for %s is a table. Ignoring non-table value (%v)", fullkey, val)
|
||||
}
|
||||
}
|
||||
return dst
|
||||
|
||||
@@ -268,7 +268,7 @@ func processImportValues(c *chart.Chart) error {
|
||||
}
|
||||
|
||||
// set the new values
|
||||
c.Values = CoalesceTables(b, cvals)
|
||||
c.Values = CoalesceTables(cvals, b)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -40,19 +40,24 @@ var (
|
||||
errMissingName = errors.New("no name provided")
|
||||
|
||||
// errInvalidName indicates that an invalid release name was provided
|
||||
errInvalidName = errors.New(fmt.Sprintf(
|
||||
errInvalidName = fmt.Errorf(
|
||||
"invalid release name, must match regex %s and the length must not be longer than 53",
|
||||
validName.String()))
|
||||
validName.String())
|
||||
|
||||
// errInvalidKubernetesName indicates that the name does not meet the Kubernetes
|
||||
// restrictions on metadata names.
|
||||
errInvalidKubernetesName = errors.New(fmt.Sprintf(
|
||||
errInvalidKubernetesName = fmt.Errorf(
|
||||
"invalid metadata name, must match regex %s and the length must not be longer than 253",
|
||||
validName.String()))
|
||||
validName.String())
|
||||
)
|
||||
|
||||
const (
|
||||
// maxNameLen is the maximum length Helm allows for a release name
|
||||
// According to the Kubernetes docs (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#rfc-1035-label-names)
|
||||
// some resource names have a max length of 63 characters while others have a max
|
||||
// length of 253 characters. As we cannot be sure the resources used in a chart, we
|
||||
// therefore need to limit it to 63 chars and reserve 10 chars for additional part to name
|
||||
// of the resource. The reason is that chart maintainers can use release name as part of
|
||||
// the resource name (and some additional chars).
|
||||
maxReleaseNameLen = 53
|
||||
// maxMetadataNameLen is the maximum length Kubernetes allows for any name.
|
||||
maxMetadataNameLen = 253
|
||||
|
||||
@@ -81,7 +81,7 @@ func New() *EnvSettings {
|
||||
KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"),
|
||||
KubeCaFile: os.Getenv("HELM_KUBECAFILE"),
|
||||
PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")),
|
||||
RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry.json")),
|
||||
RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry/config.json")),
|
||||
RepositoryConfig: envOr("HELM_REPOSITORY_CONFIG", helmpath.ConfigPath("repositories.yaml")),
|
||||
RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")),
|
||||
}
|
||||
@@ -180,6 +180,11 @@ func (s *EnvSettings) Namespace() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// SetNamespace sets the namespace in the configuration
|
||||
func (s *EnvSettings) SetNamespace(namespace string) {
|
||||
s.namespace = namespace
|
||||
}
|
||||
|
||||
// RESTClientGetter gets the kubeconfig from EnvSettings
|
||||
func (s *EnvSettings) RESTClientGetter() genericclioptions.RESTClientGetter {
|
||||
return s.config
|
||||
|
||||
@@ -23,14 +23,15 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
"helm.sh/helm/v3/internal/fileutil"
|
||||
"helm.sh/helm/v3/internal/urlutil"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/helmpath"
|
||||
"helm.sh/helm/v3/pkg/provenance"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
@@ -102,8 +103,9 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
|
||||
}
|
||||
|
||||
name := filepath.Base(u.Path)
|
||||
if u.Scheme == "oci" {
|
||||
name = fmt.Sprintf("%s-%s.tgz", name, version)
|
||||
if u.Scheme == registry.OCIScheme {
|
||||
idx := strings.LastIndexByte(name, ':')
|
||||
name = fmt.Sprintf("%s-%s.tgz", name[:idx], name[idx+1:])
|
||||
}
|
||||
|
||||
destfile := filepath.Join(dest, name)
|
||||
@@ -139,12 +141,46 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
|
||||
return destfile, ver, nil
|
||||
}
|
||||
|
||||
func (c *ChartDownloader) getOciURI(ref, version string, u *url.URL) (*url.URL, error) {
|
||||
var tag string
|
||||
var err error
|
||||
|
||||
// Evaluate whether an explicit version has been provided. Otherwise, determine version to use
|
||||
_, errSemVer := semver.NewVersion(version)
|
||||
if errSemVer == nil {
|
||||
tag = version
|
||||
} else {
|
||||
// Retrieve list of repository tags
|
||||
tags, err := c.RegistryClient.Tags(strings.TrimPrefix(ref, fmt.Sprintf("%s://", registry.OCIScheme)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(tags) == 0 {
|
||||
return nil, errors.Errorf("Unable to locate any tags in provided repository: %s", ref)
|
||||
}
|
||||
|
||||
// Determine if version provided
|
||||
// If empty, try to get the highest available tag
|
||||
// If exact version, try to find it
|
||||
// If semver constraint string, try to find a match
|
||||
tag, err = registry.GetTagMatchingVersionOrConstraint(tags, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
u.Path = fmt.Sprintf("%s:%s", u.Path, tag)
|
||||
|
||||
return u, err
|
||||
}
|
||||
|
||||
// ResolveChartVersion resolves a chart reference to a URL.
|
||||
//
|
||||
// It returns the URL and sets the ChartDownloader's Options that can fetch
|
||||
// the URL using the appropriate Getter.
|
||||
//
|
||||
// A reference may be an HTTP URL, a 'reponame/chartname' reference, or a local path.
|
||||
// A reference may be an HTTP URL, an oci reference URL, a 'reponame/chartname'
|
||||
// reference, or a local path.
|
||||
//
|
||||
// A version is a SemVer string (1.2.3-beta.1+f334a6789).
|
||||
//
|
||||
@@ -159,6 +195,10 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
|
||||
return nil, errors.Errorf("invalid chart URL format: %s", ref)
|
||||
}
|
||||
|
||||
if registry.IsOCI(u.String()) {
|
||||
return c.getOciURI(ref, version, u)
|
||||
}
|
||||
|
||||
rf, err := loadRepoConfig(c.RepositoryConfig)
|
||||
if err != nil {
|
||||
return u, err
|
||||
|
||||
174
vendor/helm.sh/helm/v3/pkg/downloader/manager.go
vendored
174
vendor/helm.sh/helm/v3/pkg/downloader/manager.go
vendored
@@ -34,7 +34,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
"helm.sh/helm/v3/internal/resolver"
|
||||
"helm.sh/helm/v3/internal/third_party/dep/fs"
|
||||
"helm.sh/helm/v3/internal/urlutil"
|
||||
@@ -43,6 +42,7 @@ import (
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/helmpath"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
@@ -232,7 +232,7 @@ func (m *Manager) loadChartDir() (*chart.Chart, error) {
|
||||
//
|
||||
// This returns a lock file, which has all of the dependencies normalized to a specific version.
|
||||
func (m *Manager) resolve(req []*chart.Dependency, repoNames map[string]string) (*chart.Lock, error) {
|
||||
res := resolver.New(m.ChartPath, m.RepositoryCache)
|
||||
res := resolver.New(m.ChartPath, m.RepositoryCache, m.RegistryClient)
|
||||
return res.Resolve(req, repoNames)
|
||||
}
|
||||
|
||||
@@ -249,22 +249,24 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
|
||||
destPath := filepath.Join(m.ChartPath, "charts")
|
||||
tmpPath := filepath.Join(m.ChartPath, "tmpcharts")
|
||||
|
||||
// Create 'charts' directory if it doesn't already exist.
|
||||
if fi, err := os.Stat(destPath); err != nil {
|
||||
// Check if 'charts' directory is not actally a directory. If it does not exist, create it.
|
||||
if fi, err := os.Stat(destPath); err == nil {
|
||||
if !fi.IsDir() {
|
||||
return errors.Errorf("%q is not a directory", destPath)
|
||||
}
|
||||
} else if os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(destPath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if !fi.IsDir() {
|
||||
return errors.Errorf("%q is not a directory", destPath)
|
||||
} else {
|
||||
return fmt.Errorf("unable to retrieve file info for '%s': %v", destPath, err)
|
||||
}
|
||||
|
||||
if err := fs.RenameWithFallback(destPath, tmpPath); err != nil {
|
||||
return errors.Wrap(err, "unable to move current charts to tmp dir")
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(destPath, 0755); err != nil {
|
||||
// Prepare tmpPath
|
||||
if err := os.MkdirAll(tmpPath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpPath)
|
||||
|
||||
fmt.Fprintf(m.Out, "Saving %d charts\n", len(deps))
|
||||
var saveError error
|
||||
@@ -273,24 +275,25 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
|
||||
// No repository means the chart is in charts directory
|
||||
if dep.Repository == "" {
|
||||
fmt.Fprintf(m.Out, "Dependency %s did not declare a repository. Assuming it exists in the charts directory\n", dep.Name)
|
||||
chartPath := filepath.Join(tmpPath, dep.Name)
|
||||
// NOTE: we are only validating the local dependency conforms to the constraints. No copying to tmpPath is necessary.
|
||||
chartPath := filepath.Join(destPath, dep.Name)
|
||||
ch, err := loader.LoadDir(chartPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to load chart: %v", err)
|
||||
return fmt.Errorf("unable to load chart '%s': %v", chartPath, err)
|
||||
}
|
||||
|
||||
constraint, err := semver.NewConstraint(dep.Version)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Dependency %s has an invalid version/constraint format: %s", dep.Name, err)
|
||||
return fmt.Errorf("dependency %s has an invalid version/constraint format: %s", dep.Name, err)
|
||||
}
|
||||
|
||||
v, err := semver.NewVersion(ch.Metadata.Version)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Invalid version %s for dependency %s: %s", dep.Version, dep.Name, err)
|
||||
return fmt.Errorf("invalid version %s for dependency %s: %s", dep.Version, dep.Name, err)
|
||||
}
|
||||
|
||||
if !constraint.Check(v) {
|
||||
saveError = fmt.Errorf("Dependency %s at version %s does not satisfy the constraint %s", dep.Name, ch.Metadata.Version, dep.Version)
|
||||
saveError = fmt.Errorf("dependency %s at version %s does not satisfy the constraint %s", dep.Name, ch.Metadata.Version, dep.Version)
|
||||
break
|
||||
}
|
||||
continue
|
||||
@@ -299,7 +302,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
|
||||
if m.Debug {
|
||||
fmt.Fprintf(m.Out, "Archiving %s from repo %s\n", dep.Name, dep.Repository)
|
||||
}
|
||||
ver, err := tarFromLocalDir(m.ChartPath, dep.Name, dep.Repository, dep.Version)
|
||||
ver, err := tarFromLocalDir(m.ChartPath, dep.Name, dep.Repository, dep.Version, tmpPath)
|
||||
if err != nil {
|
||||
saveError = err
|
||||
break
|
||||
@@ -329,6 +332,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
|
||||
Keyring: m.Keyring,
|
||||
RepositoryConfig: m.RepositoryConfig,
|
||||
RepositoryCache: m.RepositoryCache,
|
||||
RegistryClient: m.RegistryClient,
|
||||
Getters: m.Getters,
|
||||
Options: []getter.Option{
|
||||
getter.WithBasicAuth(username, password),
|
||||
@@ -339,12 +343,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
|
||||
}
|
||||
|
||||
version := ""
|
||||
if strings.HasPrefix(churl, "oci://") {
|
||||
if !resolver.FeatureGateOCI.IsEnabled() {
|
||||
return errors.Wrapf(resolver.FeatureGateOCI.Error(),
|
||||
"the repository %s is an OCI registry", churl)
|
||||
}
|
||||
|
||||
if registry.IsOCI(churl) {
|
||||
churl, version, err = parseOCIRef(churl)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not parse OCI reference")
|
||||
@@ -354,8 +353,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
|
||||
getter.WithTagName(version))
|
||||
}
|
||||
|
||||
_, _, err = dl.DownloadTo(churl, version, destPath)
|
||||
if err != nil {
|
||||
if _, _, err = dl.DownloadTo(churl, version, tmpPath); err != nil {
|
||||
saveError = errors.Wrapf(err, "could not download %s", churl)
|
||||
break
|
||||
}
|
||||
@@ -363,36 +361,14 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
|
||||
churls[churl] = struct{}{}
|
||||
}
|
||||
|
||||
// TODO: this should probably be refactored to be a []error, so we can capture and provide more information rather than "last error wins".
|
||||
if saveError == nil {
|
||||
fmt.Fprintln(m.Out, "Deleting outdated charts")
|
||||
for _, dep := range deps {
|
||||
// Chart from local charts directory stays in place
|
||||
if dep.Repository != "" {
|
||||
if err := m.safeDeleteDep(dep.Name, tmpPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := move(tmpPath, destPath); err != nil {
|
||||
// now we can move all downloaded charts to destPath and delete outdated dependencies
|
||||
if err := m.safeMoveDeps(deps, tmpPath, destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(tmpPath); err != nil {
|
||||
return errors.Wrapf(err, "failed to remove %v", tmpPath)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(m.Out, "Save error occurred: ", saveError)
|
||||
fmt.Fprintln(m.Out, "Deleting newly downloaded charts, restoring pre-update state")
|
||||
for _, dep := range deps {
|
||||
if err := m.safeDeleteDep(dep.Name, destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := os.RemoveAll(destPath); err != nil {
|
||||
return errors.Wrapf(err, "failed to remove %v", destPath)
|
||||
}
|
||||
if err := fs.RenameWithFallback(tmpPath, destPath); err != nil {
|
||||
return errors.Wrap(err, "unable to move current charts to tmp dir")
|
||||
}
|
||||
return saveError
|
||||
}
|
||||
return nil
|
||||
@@ -410,36 +386,76 @@ func parseOCIRef(chartRef string) (string, string, error) {
|
||||
return chartRef, tag, nil
|
||||
}
|
||||
|
||||
// safeDeleteDep deletes any versions of the given dependency in the given directory.
|
||||
// safeMoveDep moves all dependencies in the source and moves them into dest.
|
||||
//
|
||||
// It does this by first matching the file name to an expected pattern, then loading
|
||||
// the file to verify that it is a chart with the same name as the given name.
|
||||
// the file to verify that it is a chart.
|
||||
//
|
||||
// Because it requires tar file introspection, it is more intensive than a basic delete.
|
||||
// Any charts in dest that do not exist in source are removed (barring local dependencies)
|
||||
//
|
||||
// Because it requires tar file introspection, it is more intensive than a basic move.
|
||||
//
|
||||
// This will only return errors that should stop processing entirely. Other errors
|
||||
// will emit log messages or be ignored.
|
||||
func (m *Manager) safeDeleteDep(name, dir string) error {
|
||||
files, err := filepath.Glob(filepath.Join(dir, name+"-*.tgz"))
|
||||
func (m *Manager) safeMoveDeps(deps []*chart.Dependency, source, dest string) error {
|
||||
existsInSourceDirectory := map[string]bool{}
|
||||
isLocalDependency := map[string]bool{}
|
||||
sourceFiles, err := os.ReadDir(source)
|
||||
if err != nil {
|
||||
// Only for ErrBadPattern
|
||||
return err
|
||||
}
|
||||
for _, fname := range files {
|
||||
ch, err := loader.LoadFile(fname)
|
||||
if err != nil {
|
||||
fmt.Fprintf(m.Out, "Could not verify %s for deletion: %s (Skipping)", fname, err)
|
||||
// attempt to read destFiles; fail fast if we can't
|
||||
destFiles, err := os.ReadDir(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, dep := range deps {
|
||||
if dep.Repository == "" {
|
||||
isLocalDependency[dep.Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range sourceFiles {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
if ch.Name() != name {
|
||||
// This is not the file you are looking for.
|
||||
filename := file.Name()
|
||||
sourcefile := filepath.Join(source, filename)
|
||||
destfile := filepath.Join(dest, filename)
|
||||
existsInSourceDirectory[filename] = true
|
||||
if _, err := loader.LoadFile(sourcefile); err != nil {
|
||||
fmt.Fprintf(m.Out, "Could not verify %s for moving: %s (Skipping)", sourcefile, err)
|
||||
continue
|
||||
}
|
||||
if err := os.Remove(fname); err != nil {
|
||||
fmt.Fprintf(m.Out, "Could not delete %s: %s (Skipping)", fname, err)
|
||||
// NOTE: no need to delete the dest; os.Rename replaces it.
|
||||
if err := fs.RenameWithFallback(sourcefile, destfile); err != nil {
|
||||
fmt.Fprintf(m.Out, "Unable to move %s to charts dir %s (Skipping)", sourcefile, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintln(m.Out, "Deleting outdated charts")
|
||||
// find all files that exist in dest that do not exist in source; delete them (outdated dependencies)
|
||||
for _, file := range destFiles {
|
||||
if !file.IsDir() && !existsInSourceDirectory[file.Name()] {
|
||||
fname := filepath.Join(dest, file.Name())
|
||||
ch, err := loader.LoadFile(fname)
|
||||
if err != nil {
|
||||
fmt.Fprintf(m.Out, "Could not verify %s for deletion: %s (Skipping)\n", fname, err)
|
||||
continue
|
||||
}
|
||||
// local dependency - skip
|
||||
if isLocalDependency[ch.Name()] {
|
||||
continue
|
||||
}
|
||||
if err := os.Remove(fname); err != nil {
|
||||
fmt.Fprintf(m.Out, "Could not delete %s: %s (Skipping)", fname, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -457,7 +473,7 @@ func (m *Manager) hasAllRepos(deps []*chart.Dependency) error {
|
||||
Loop:
|
||||
for _, dd := range deps {
|
||||
// If repo is from local path or OCI, continue
|
||||
if strings.HasPrefix(dd.Repository, "file://") || strings.HasPrefix(dd.Repository, "oci://") {
|
||||
if strings.HasPrefix(dd.Repository, "file://") || registry.IsOCI(dd.Repository) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -558,8 +574,7 @@ func (m *Manager) resolveRepoNames(deps []*chart.Dependency) (map[string]string,
|
||||
missing := []string{}
|
||||
for _, dd := range deps {
|
||||
// Don't map the repository, we don't need to download chart from charts directory
|
||||
// When OCI is used there is no Helm repository
|
||||
if dd.Repository == "" || strings.HasPrefix(dd.Repository, "oci://") {
|
||||
if dd.Repository == "" {
|
||||
continue
|
||||
}
|
||||
// if dep chart is from local path, verify the path is valid
|
||||
@@ -575,7 +590,7 @@ func (m *Manager) resolveRepoNames(deps []*chart.Dependency) (map[string]string,
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(dd.Repository, "oci://") {
|
||||
if registry.IsOCI(dd.Repository) {
|
||||
reposMap[dd.Name] = dd.Repository
|
||||
continue
|
||||
}
|
||||
@@ -689,7 +704,7 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
|
||||
//
|
||||
// If it finds a URL that is "relative", it will prepend the repoURL.
|
||||
func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, insecureskiptlsverify, passcredentialsall bool, caFile, certFile, keyFile string, err error) {
|
||||
if strings.HasPrefix(repoURL, "oci://") {
|
||||
if registry.IsOCI(repoURL) {
|
||||
return fmt.Sprintf("%s/%s:%s", repoURL, name, version), "", "", false, false, "", "", "", nil
|
||||
}
|
||||
|
||||
@@ -783,6 +798,7 @@ func normalizeURL(baseURL, urlOrPath string) (string, error) {
|
||||
return urlOrPath, errors.Wrap(err, "base URL failed to parse")
|
||||
}
|
||||
|
||||
u2.RawPath = path.Join(u2.RawPath, urlOrPath)
|
||||
u2.Path = path.Join(u2.Path, urlOrPath)
|
||||
return u2.String(), nil
|
||||
}
|
||||
@@ -832,10 +848,8 @@ func writeLock(chartpath string, lock *chart.Lock, legacyLockfile bool) error {
|
||||
return ioutil.WriteFile(dest, data, 0644)
|
||||
}
|
||||
|
||||
// archive a dep chart from local directory and save it into charts/
|
||||
func tarFromLocalDir(chartpath, name, repo, version string) (string, error) {
|
||||
destPath := filepath.Join(chartpath, "charts")
|
||||
|
||||
// archive a dep chart from local directory and save it into destPath
|
||||
func tarFromLocalDir(chartpath, name, repo, version, destPath string) (string, error) {
|
||||
if !strings.HasPrefix(repo, "file://") {
|
||||
return "", errors.Errorf("wrong format: chart %s repository %s", name, repo)
|
||||
}
|
||||
@@ -868,20 +882,6 @@ func tarFromLocalDir(chartpath, name, repo, version string) (string, error) {
|
||||
return "", errors.Errorf("can't get a valid version for dependency %s", name)
|
||||
}
|
||||
|
||||
// move files from tmppath to destpath
|
||||
func move(tmpPath, destPath string) error {
|
||||
files, _ := ioutil.ReadDir(tmpPath)
|
||||
for _, file := range files {
|
||||
filename := file.Name()
|
||||
tmpfile := filepath.Join(tmpPath, filename)
|
||||
destfile := filepath.Join(destPath, filename)
|
||||
if err := fs.RenameWithFallback(tmpfile, destfile); err != nil {
|
||||
return errors.Wrap(err, "unable to move local charts to charts dir")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The prefix to use for cache keys created by the manager for repo names
|
||||
const managerKeyPrefix = "helm-manager-"
|
||||
|
||||
|
||||
17
vendor/helm.sh/helm/v3/pkg/engine/engine.go
vendored
17
vendor/helm.sh/helm/v3/pkg/engine/engine.go
vendored
@@ -97,7 +97,7 @@ const warnStartDelim = "HELM_ERR_START"
|
||||
const warnEndDelim = "HELM_ERR_END"
|
||||
const recursionMaxNums = 1000
|
||||
|
||||
var warnRegex = regexp.MustCompile(warnStartDelim + `(.*)` + warnEndDelim)
|
||||
var warnRegex = regexp.MustCompile(warnStartDelim + `((?s).*)` + warnEndDelim)
|
||||
|
||||
func warnWrap(warn string) string {
|
||||
return warnStartDelim + warn + warnEndDelim
|
||||
@@ -344,13 +344,20 @@ func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable {
|
||||
//
|
||||
// As it recurses, it also sets the values to be appropriate for the template
|
||||
// scope.
|
||||
func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil.Values) {
|
||||
func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil.Values) map[string]interface{} {
|
||||
subCharts := make(map[string]interface{})
|
||||
chartMetaData := struct {
|
||||
chart.Metadata
|
||||
IsRoot bool
|
||||
}{*c.Metadata, c.IsRoot()}
|
||||
|
||||
next := map[string]interface{}{
|
||||
"Chart": c.Metadata,
|
||||
"Chart": chartMetaData,
|
||||
"Files": newFiles(c.Files),
|
||||
"Release": vals["Release"],
|
||||
"Capabilities": vals["Capabilities"],
|
||||
"Values": make(chartutil.Values),
|
||||
"Subcharts": subCharts,
|
||||
}
|
||||
|
||||
// If there is a {{.Values.ThisChart}} in the parent metadata,
|
||||
@@ -362,7 +369,7 @@ func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil.
|
||||
}
|
||||
|
||||
for _, child := range c.Dependencies() {
|
||||
recAllTpls(child, templates, next)
|
||||
subCharts[child.Name()] = recAllTpls(child, templates, next)
|
||||
}
|
||||
|
||||
newParentID := c.ChartFullPath()
|
||||
@@ -376,6 +383,8 @@ func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil.
|
||||
basePath: path.Join(newParentID, "templates"),
|
||||
}
|
||||
}
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
// isTemplateValid returns true if the template is valid for the chart type
|
||||
|
||||
@@ -63,7 +63,7 @@ func NewLookupFunction(config *rest.Config) lookupFunc {
|
||||
}
|
||||
return obj.UnstructuredContent(), nil
|
||||
}
|
||||
//this will return a list
|
||||
// this will return a list
|
||||
obj, err := client.List(context.Background(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
@@ -112,7 +112,7 @@ func getAPIResourceForGVK(gvk schema.GroupVersionKind, config *rest.Config) (met
|
||||
return res, err
|
||||
}
|
||||
for _, resource := range resList.APIResources {
|
||||
//if a resource contains a "/" it's referencing a subresource. we don't support suberesource for now.
|
||||
// if a resource contains a "/" it's referencing a subresource. we don't support suberesource for now.
|
||||
if resource.Kind == gvk.Kind && !strings.Contains(resource.Name, "/") {
|
||||
res = resource
|
||||
res.Group = gvk.Group
|
||||
|
||||
38
vendor/helm.sh/helm/v3/pkg/gates/gates.go
vendored
38
vendor/helm.sh/helm/v3/pkg/gates/gates.go
vendored
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package gates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Gate is the name of the feature gate.
|
||||
type Gate string
|
||||
|
||||
// String returns the string representation of this feature gate.
|
||||
func (g Gate) String() string {
|
||||
return string(g)
|
||||
}
|
||||
|
||||
// IsEnabled determines whether a certain feature gate is enabled.
|
||||
func (g Gate) IsEnabled() bool {
|
||||
return os.Getenv(string(g)) != ""
|
||||
}
|
||||
|
||||
func (g Gate) Error() error {
|
||||
return fmt.Errorf("this feature has been marked as experimental and is not enabled by default. Please set %s=1 in your environment to use this feature", g.String())
|
||||
}
|
||||
13
vendor/helm.sh/helm/v3/pkg/getter/getter.go
vendored
13
vendor/helm.sh/helm/v3/pkg/getter/getter.go
vendored
@@ -18,12 +18,13 @@ package getter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
)
|
||||
|
||||
// options are generic parameters to be provided to the getter during instantiation.
|
||||
@@ -43,6 +44,7 @@ type options struct {
|
||||
version string
|
||||
registryClient *registry.Client
|
||||
timeout time.Duration
|
||||
transport *http.Transport
|
||||
}
|
||||
|
||||
// Option allows specifying various settings configurable by the user for overriding the defaults
|
||||
@@ -119,6 +121,13 @@ func WithUntar() Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithTransport sets the http.Transport to allow overwriting the HTTPGetter default.
|
||||
func WithTransport(transport *http.Transport) Option {
|
||||
return func(opts *options) {
|
||||
opts.transport = transport
|
||||
}
|
||||
}
|
||||
|
||||
// Getter is an interface to support GET to the specified URL.
|
||||
type Getter interface {
|
||||
// Get file content by url string
|
||||
@@ -169,7 +178,7 @@ var httpProvider = Provider{
|
||||
}
|
||||
|
||||
var ociProvider = Provider{
|
||||
Schemes: []string{"oci"},
|
||||
Schemes: []string{registry.OCIScheme},
|
||||
New: NewOCIGetter,
|
||||
}
|
||||
|
||||
|
||||
52
vendor/helm.sh/helm/v3/pkg/getter/httpgetter.go
vendored
52
vendor/helm.sh/helm/v3/pkg/getter/httpgetter.go
vendored
@@ -21,6 +21,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@@ -31,10 +32,12 @@ import (
|
||||
|
||||
// HTTPGetter is the default HTTP(/S) backend handler
|
||||
type HTTPGetter struct {
|
||||
opts options
|
||||
opts options
|
||||
transport *http.Transport
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
//Get performs a Get from repo.Getter and returns the body.
|
||||
// Get performs a Get from repo.Getter and returns the body.
|
||||
func (g *HTTPGetter) Get(href string, options ...Option) (*bytes.Buffer, error) {
|
||||
for _, opt := range options {
|
||||
opt(&g.opts)
|
||||
@@ -43,13 +46,11 @@ func (g *HTTPGetter) Get(href string, options ...Option) (*bytes.Buffer, error)
|
||||
}
|
||||
|
||||
func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
// Set a helm specific user agent so that a repo server and metrics can
|
||||
// separate helm calls from other tools interacting with repos.
|
||||
req, err := http.NewRequest("GET", href, nil)
|
||||
req, err := http.NewRequest(http.MethodGet, href, nil)
|
||||
if err != nil {
|
||||
return buf, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", version.GetUserAgent())
|
||||
@@ -61,11 +62,11 @@ func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) {
|
||||
// with the basic auth is the one being fetched.
|
||||
u1, err := url.Parse(g.opts.url)
|
||||
if err != nil {
|
||||
return buf, errors.Wrap(err, "Unable to parse getter URL")
|
||||
return nil, errors.Wrap(err, "Unable to parse getter URL")
|
||||
}
|
||||
u2, err := url.Parse(href)
|
||||
if err != nil {
|
||||
return buf, errors.Wrap(err, "Unable to parse URL getting from")
|
||||
return nil, errors.Wrap(err, "Unable to parse URL getting from")
|
||||
}
|
||||
|
||||
// Host on URL (returned from url.Parse) contains the port if present.
|
||||
@@ -84,14 +85,15 @@ func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) {
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return buf, err
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return buf, errors.Errorf("failed to fetch %s : %s", href, resp.Status)
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.Errorf("failed to fetch %s : %s", href, resp.Status)
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
_, err = io.Copy(buf, resp.Body)
|
||||
resp.Body.Close()
|
||||
return buf, err
|
||||
}
|
||||
|
||||
@@ -107,10 +109,20 @@ func NewHTTPGetter(options ...Option) (Getter, error) {
|
||||
}
|
||||
|
||||
func (g *HTTPGetter) httpClient() (*http.Client, error) {
|
||||
transport := &http.Transport{
|
||||
DisableCompression: true,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
if g.opts.transport != nil {
|
||||
return &http.Client{
|
||||
Transport: g.opts.transport,
|
||||
Timeout: g.opts.timeout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
g.once.Do(func() {
|
||||
g.transport = &http.Transport{
|
||||
DisableCompression: true,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
})
|
||||
|
||||
if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" {
|
||||
tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile)
|
||||
if err != nil {
|
||||
@@ -124,21 +136,21 @@ func (g *HTTPGetter) httpClient() (*http.Client, error) {
|
||||
}
|
||||
tlsConf.ServerName = sni
|
||||
|
||||
transport.TLSClientConfig = tlsConf
|
||||
g.transport.TLSClientConfig = tlsConf
|
||||
}
|
||||
|
||||
if g.opts.insecureSkipVerifyTLS {
|
||||
if transport.TLSClientConfig == nil {
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
if g.transport.TLSClientConfig == nil {
|
||||
g.transport.TLSClientConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
} else {
|
||||
transport.TLSClientConfig.InsecureSkipVerify = true
|
||||
g.transport.TLSClientConfig.InsecureSkipVerify = true
|
||||
}
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
Transport: g.transport,
|
||||
Timeout: g.opts.timeout,
|
||||
}
|
||||
|
||||
|
||||
26
vendor/helm.sh/helm/v3/pkg/getter/ocigetter.go
vendored
26
vendor/helm.sh/helm/v3/pkg/getter/ocigetter.go
vendored
@@ -20,7 +20,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
)
|
||||
|
||||
// OCIGetter is the default HTTP(/S) backend handler
|
||||
@@ -28,7 +28,7 @@ type OCIGetter struct {
|
||||
opts options
|
||||
}
|
||||
|
||||
//Get performs a Get from repo.Getter and returns the body.
|
||||
// Get performs a Get from repo.Getter and returns the body.
|
||||
func (g *OCIGetter) Get(href string, options ...Option) (*bytes.Buffer, error) {
|
||||
for _, opt := range options {
|
||||
opt(&g.opts)
|
||||
@@ -39,22 +39,26 @@ func (g *OCIGetter) Get(href string, options ...Option) (*bytes.Buffer, error) {
|
||||
func (g *OCIGetter) get(href string) (*bytes.Buffer, error) {
|
||||
client := g.opts.registryClient
|
||||
|
||||
ref := strings.TrimPrefix(href, "oci://")
|
||||
if version := g.opts.version; version != "" {
|
||||
ref = fmt.Sprintf("%s:%s", ref, version)
|
||||
ref := strings.TrimPrefix(href, fmt.Sprintf("%s://", registry.OCIScheme))
|
||||
|
||||
var pullOpts []registry.PullOption
|
||||
requestingProv := strings.HasSuffix(ref, ".prov")
|
||||
if requestingProv {
|
||||
ref = strings.TrimSuffix(ref, ".prov")
|
||||
pullOpts = append(pullOpts,
|
||||
registry.PullOptWithChart(false),
|
||||
registry.PullOptWithProv(true))
|
||||
}
|
||||
|
||||
r, err := registry.ParseReference(ref)
|
||||
result, err := client.Pull(ref, pullOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf, err := client.PullChart(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if requestingProv {
|
||||
return bytes.NewBuffer(result.Prov.Data), nil
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
return bytes.NewBuffer(result.Chart.Data), nil
|
||||
}
|
||||
|
||||
// NewOCIGetter constructs a valid http/https client as a Getter
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build darwin
|
||||
//go:build darwin
|
||||
|
||||
package helmpath
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build !windows,!darwin
|
||||
//go:build !windows && !darwin
|
||||
|
||||
package helmpath
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build windows
|
||||
//go:build windows
|
||||
|
||||
package helmpath
|
||||
|
||||
|
||||
30
vendor/helm.sh/helm/v3/pkg/kube/client.go
vendored
30
vendor/helm.sh/helm/v3/pkg/kube/client.go
vendored
@@ -162,6 +162,15 @@ func (c *Client) WaitWithJobs(resources ResourceList, timeout time.Duration) err
|
||||
return w.waitForResources(resources)
|
||||
}
|
||||
|
||||
// WaitForDelete wait up to the given timeout for the specified resources to be deleted.
|
||||
func (c *Client) WaitForDelete(resources ResourceList, timeout time.Duration) error {
|
||||
w := waiter{
|
||||
log: c.Log,
|
||||
timeout: timeout,
|
||||
}
|
||||
return w.waitForDeletedResources(resources)
|
||||
}
|
||||
|
||||
func (c *Client) namespace() string {
|
||||
if c.Namespace != "" {
|
||||
return c.Namespace
|
||||
@@ -183,7 +192,18 @@ func (c *Client) newBuilder() *resource.Builder {
|
||||
|
||||
// Build validates for Kubernetes objects and returns unstructured infos.
|
||||
func (c *Client) Build(reader io.Reader, validate bool) (ResourceList, error) {
|
||||
schema, err := c.Factory.Validator(validate)
|
||||
validationDirective := metav1.FieldValidationIgnore
|
||||
if validate {
|
||||
validationDirective = metav1.FieldValidationStrict
|
||||
}
|
||||
|
||||
dynamicClient, err := c.Factory.DynamicClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
verifier := resource.NewQueryParamVerifier(dynamicClient, c.Factory.OpenAPIGetter(), resource.QueryParamFieldValidation)
|
||||
schema, err := c.Factory.Validator(validationDirective, verifier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -255,7 +275,7 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err
|
||||
}
|
||||
|
||||
for _, info := range original.Difference(target) {
|
||||
c.Log("Deleting %q in %s...", info.Name, info.Namespace)
|
||||
c.Log("Deleting %s %q in namespace %s...", info.Mapping.GroupVersionKind.Kind, info.Name, info.Namespace)
|
||||
|
||||
if err := info.Get(); err != nil {
|
||||
c.Log("Unable to get obj %q, err: %s", info.Name, err)
|
||||
@@ -489,7 +509,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object,
|
||||
}
|
||||
|
||||
if patch == nil || string(patch) == "{}" {
|
||||
c.Log("Looks like there are no changes for %s %q", target.Mapping.GroupVersionKind.Kind, target.Name)
|
||||
c.Log("Looks like there are no changes for %s %q", kind, target.Name)
|
||||
// This needs to happen to make sure that Helm has the latest info from the API
|
||||
// Otherwise there will be no labels and other functions that use labels will panic
|
||||
if err := target.Get(); err != nil {
|
||||
@@ -498,6 +518,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object,
|
||||
return nil
|
||||
}
|
||||
// send patch to server
|
||||
c.Log("Patch %s %q in namespace %s", kind, target.Name, target.Namespace)
|
||||
obj, err = helper.Patch(target.Namespace, target.Name, patchType, patch, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot patch %q with kind %s", target.Name, kind)
|
||||
@@ -636,6 +657,9 @@ func (c *Client) WaitAndGetCompletedPodPhase(name string, timeout time.Duration)
|
||||
FieldSelector: fmt.Sprintf("metadata.name=%s", name),
|
||||
TimeoutSeconds: &to,
|
||||
})
|
||||
if err != nil {
|
||||
return v1.PodUnknown, err
|
||||
}
|
||||
|
||||
for event := range watcher.ResultChan() {
|
||||
p, ok := event.Object.(*v1.Pod)
|
||||
|
||||
12
vendor/helm.sh/helm/v3/pkg/kube/factory.go
vendored
12
vendor/helm.sh/helm/v3/pkg/kube/factory.go
vendored
@@ -18,6 +18,8 @@ package kube // import "helm.sh/helm/v3/pkg/kube"
|
||||
|
||||
import (
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kubectl/pkg/validation"
|
||||
@@ -28,11 +30,19 @@ import (
|
||||
type Factory interface {
|
||||
// ToRawKubeConfigLoader return kubeconfig loader as-is
|
||||
ToRawKubeConfigLoader() clientcmd.ClientConfig
|
||||
|
||||
// DynamicClient returns a dynamic client ready for use
|
||||
DynamicClient() (dynamic.Interface, error)
|
||||
|
||||
// KubernetesClientSet gives you back an external clientset
|
||||
KubernetesClientSet() (*kubernetes.Clientset, error)
|
||||
|
||||
// NewBuilder returns an object that assists in loading objects from both disk and the server
|
||||
// and which implements the common patterns for CLI interactions with generic resources.
|
||||
NewBuilder() *resource.Builder
|
||||
|
||||
// Returns a schema that can validate objects stored on disk.
|
||||
Validator(validate bool) (validation.Schema, error)
|
||||
Validator(validationDirective string, verifier *resource.QueryParamVerifier) (validation.Schema, error)
|
||||
// OpenAPIGetter returns a getter for the openapi schema document
|
||||
OpenAPIGetter() discovery.OpenAPISchemaInterface
|
||||
}
|
||||
|
||||
14
vendor/helm.sh/helm/v3/pkg/kube/fake/fake.go
vendored
14
vendor/helm.sh/helm/v3/pkg/kube/fake/fake.go
vendored
@@ -40,6 +40,7 @@ type FailingKubeClient struct {
|
||||
BuildError error
|
||||
BuildUnstructuredError error
|
||||
WaitAndGetCompletedPodPhaseError error
|
||||
WaitDuration time.Duration
|
||||
}
|
||||
|
||||
// Create returns the configured error if set or prints
|
||||
@@ -50,8 +51,9 @@ func (f *FailingKubeClient) Create(resources kube.ResourceList) (*kube.Result, e
|
||||
return f.PrintingKubeClient.Create(resources)
|
||||
}
|
||||
|
||||
// Wait returns the configured error if set or prints
|
||||
// Waits the amount of time defined on f.WaitDuration, then returns the configured error if set or prints.
|
||||
func (f *FailingKubeClient) Wait(resources kube.ResourceList, d time.Duration) error {
|
||||
time.Sleep(f.WaitDuration)
|
||||
if f.WaitError != nil {
|
||||
return f.WaitError
|
||||
}
|
||||
@@ -63,7 +65,15 @@ func (f *FailingKubeClient) WaitWithJobs(resources kube.ResourceList, d time.Dur
|
||||
if f.WaitError != nil {
|
||||
return f.WaitError
|
||||
}
|
||||
return f.PrintingKubeClient.Wait(resources, d)
|
||||
return f.PrintingKubeClient.WaitWithJobs(resources, d)
|
||||
}
|
||||
|
||||
// WaitForDelete returns the configured error if set or prints
|
||||
func (f *FailingKubeClient) WaitForDelete(resources kube.ResourceList, d time.Duration) error {
|
||||
if f.WaitError != nil {
|
||||
return f.WaitError
|
||||
}
|
||||
return f.PrintingKubeClient.WaitForDelete(resources, d)
|
||||
}
|
||||
|
||||
// Delete returns the configured error if set or prints
|
||||
|
||||
@@ -57,6 +57,11 @@ func (p *PrintingKubeClient) WaitWithJobs(resources kube.ResourceList, _ time.Du
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *PrintingKubeClient) WaitForDelete(resources kube.ResourceList, _ time.Duration) error {
|
||||
_, err := io.Copy(p.Out, bufferize(resources))
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete implements KubeClient delete.
|
||||
//
|
||||
// It only prints out the content to be deleted.
|
||||
|
||||
9
vendor/helm.sh/helm/v3/pkg/kube/interface.go
vendored
9
vendor/helm.sh/helm/v3/pkg/kube/interface.go
vendored
@@ -70,4 +70,13 @@ type Interface interface {
|
||||
IsReachable() error
|
||||
}
|
||||
|
||||
// InterfaceExt is introduced to avoid breaking backwards compatibility for Interface implementers.
|
||||
//
|
||||
// TODO Helm 4: Remove InterfaceExt and integrate its method(s) into the Interface.
|
||||
type InterfaceExt interface {
|
||||
// WaitForDelete wait up to the given timeout for the specified resources to be deleted.
|
||||
WaitForDelete(resources ResourceList, timeout time.Duration) error
|
||||
}
|
||||
|
||||
var _ Interface = (*Client)(nil)
|
||||
var _ InterfaceExt = (*Client)(nil)
|
||||
|
||||
4
vendor/helm.sh/helm/v3/pkg/kube/ready.go
vendored
4
vendor/helm.sh/helm/v3/pkg/kube/ready.go
vendored
@@ -227,7 +227,7 @@ func (c *ReadyChecker) jobReady(job *batchv1.Job) bool {
|
||||
c.log("Job is failed: %s/%s", job.GetNamespace(), job.GetName())
|
||||
return false
|
||||
}
|
||||
if job.Status.Succeeded < *job.Spec.Completions {
|
||||
if job.Spec.Completions != nil && job.Status.Succeeded < *job.Spec.Completions {
|
||||
c.log("Job is not completed: %s/%s", job.GetNamespace(), job.GetName())
|
||||
return false
|
||||
}
|
||||
@@ -377,7 +377,7 @@ func (c *ReadyChecker) statefulSetReady(sts *appsv1.StatefulSet) bool {
|
||||
expectedReplicas := replicas - partition
|
||||
|
||||
// Make sure all the updated pods have been scheduled
|
||||
if int(sts.Status.UpdatedReplicas) != expectedReplicas {
|
||||
if int(sts.Status.UpdatedReplicas) < expectedReplicas {
|
||||
c.log("StatefulSet is not ready: %s/%s. %d out of %d expected pods have been scheduled", sts.Namespace, sts.Name, sts.Status.UpdatedReplicas, expectedReplicas)
|
||||
return false
|
||||
}
|
||||
|
||||
19
vendor/helm.sh/helm/v3/pkg/kube/wait.go
vendored
19
vendor/helm.sh/helm/v3/pkg/kube/wait.go
vendored
@@ -28,6 +28,7 @@ import (
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -60,6 +61,24 @@ func (w *waiter) waitForResources(created ResourceList) error {
|
||||
}, ctx.Done())
|
||||
}
|
||||
|
||||
// waitForDeletedResources polls to check if all the resources are deleted or a timeout is reached
|
||||
func (w *waiter) waitForDeletedResources(deleted ResourceList) error {
|
||||
w.log("beginning wait for %d resources to be deleted with timeout of %v", len(deleted), w.timeout)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), w.timeout)
|
||||
defer cancel()
|
||||
|
||||
return wait.PollImmediateUntil(2*time.Second, func() (bool, error) {
|
||||
for _, v := range deleted {
|
||||
err := v.Get()
|
||||
if err == nil || !apierrors.IsNotFound(err) {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}, ctx.Done())
|
||||
}
|
||||
|
||||
// SelectorsForObject returns the pod label selector for a given object
|
||||
//
|
||||
// Modified version of https://github.com/kubernetes/kubernetes/blob/v1.14.1/pkg/kubectl/polymorphichelpers/helpers.go#L84
|
||||
|
||||
@@ -18,6 +18,7 @@ package rules // import "helm.sh/helm/v3/pkg/lint/rules"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@@ -25,11 +26,12 @@ import (
|
||||
kscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
)
|
||||
|
||||
const (
|
||||
var (
|
||||
// This should be set in the Makefile based on the version of client-go being imported.
|
||||
// These constants will be overwritten with LDFLAGS
|
||||
k8sVersionMajor = 1
|
||||
k8sVersionMinor = 20
|
||||
// These constants will be overwritten with LDFLAGS. The version components must be
|
||||
// strings in order for LDFLAGS to set them.
|
||||
k8sVersionMajor = "1"
|
||||
k8sVersionMinor = "20"
|
||||
)
|
||||
|
||||
// deprecatedAPIError indicates than an API is deprecated in Kubernetes
|
||||
@@ -60,7 +62,16 @@ func validateNoDeprecations(resource *K8sYamlStruct) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
if !deprecation.IsDeprecated(runtimeObject, k8sVersionMajor, k8sVersionMinor) {
|
||||
maj, err := strconv.Atoi(k8sVersionMajor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
min, err := strconv.Atoi(k8sVersionMinor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !deprecation.IsDeprecated(runtimeObject, maj, min) {
|
||||
return nil
|
||||
}
|
||||
gvk := fmt.Sprintf("%s %s", resource.APIVersion, resource.Kind)
|
||||
|
||||
@@ -70,6 +70,12 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace
|
||||
Namespace: namespace,
|
||||
}
|
||||
|
||||
// lint ignores import-values
|
||||
// See https://github.com/helm/helm/issues/9658
|
||||
if err := chartutil.ProcessDependencies(chart, values); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cvals, err := chartutil.CoalesceValues(chart, values)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -113,7 +119,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace
|
||||
|
||||
// NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1463
|
||||
// Check that all the templates have a matching value
|
||||
//linter.RunLinterRule(support.WarningSev, fpath, validateNoMissingValues(templatesPath, valuesToRender, preExecutedTemplate))
|
||||
// linter.RunLinterRule(support.WarningSev, fpath, validateNoMissingValues(templatesPath, valuesToRender, preExecutedTemplate))
|
||||
|
||||
// NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1037
|
||||
// linter.RunLinterRule(support.WarningSev, fpath, validateQuotes(string(preExecutedTemplate)))
|
||||
@@ -146,6 +152,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace
|
||||
linter.RunLinterRule(support.WarningSev, fpath, validateNoDeprecations(yamlStruct))
|
||||
|
||||
linter.RunLinterRule(support.ErrorSev, fpath, validateMatchSelector(yamlStruct, renderedContent))
|
||||
linter.RunLinterRule(support.ErrorSev, fpath, validateListAnnotations(yamlStruct, renderedContent))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,6 +300,28 @@ func validateMatchSelector(yamlStruct *K8sYamlStruct, manifest string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func validateListAnnotations(yamlStruct *K8sYamlStruct, manifest string) error {
|
||||
if yamlStruct.Kind == "List" {
|
||||
m := struct {
|
||||
Items []struct {
|
||||
Metadata struct {
|
||||
Annotations map[string]string
|
||||
}
|
||||
}
|
||||
}{}
|
||||
|
||||
if err := yaml.Unmarshal([]byte(manifest), &m); err != nil {
|
||||
return validateYamlContent(err)
|
||||
}
|
||||
|
||||
for _, i := range m.Items {
|
||||
if _, ok := i.Metadata.Annotations["helm.sh/resource-policy"]; ok {
|
||||
return errors.New("Annotation 'helm.sh/resource-policy' within List objects are ignored")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// K8sYamlStruct stubs a Kubernetes YAML file.
|
||||
//
|
||||
|
||||
2
vendor/helm.sh/helm/v3/pkg/plugin/plugin.go
vendored
2
vendor/helm.sh/helm/v3/pkg/plugin/plugin.go
vendored
@@ -152,7 +152,7 @@ func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string, error) {
|
||||
parts = strings.Split(os.ExpandEnv(p.Metadata.Command), " ")
|
||||
}
|
||||
if len(parts) == 0 || parts[0] == "" {
|
||||
return "", nil, fmt.Errorf("No plugin command is applicable")
|
||||
return "", nil, fmt.Errorf("no plugin command is applicable")
|
||||
}
|
||||
|
||||
main := parts[0]
|
||||
|
||||
@@ -27,23 +27,24 @@ import (
|
||||
|
||||
type execRender struct {
|
||||
binaryPath string
|
||||
args []string
|
||||
}
|
||||
|
||||
// NewExec returns a PostRenderer implementation that calls the provided binary.
|
||||
// It returns an error if the binary cannot be found. If the path does not
|
||||
// contain any separators, it will search in $PATH, otherwise it will resolve
|
||||
// any relative paths to a fully qualified path
|
||||
func NewExec(binaryPath string) (PostRenderer, error) {
|
||||
func NewExec(binaryPath string, args ...string) (PostRenderer, error) {
|
||||
fullPath, err := getFullPath(binaryPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &execRender{fullPath}, nil
|
||||
return &execRender{fullPath, args}, nil
|
||||
}
|
||||
|
||||
// Run the configured binary for the post render
|
||||
func (p *execRender) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
cmd := exec.Command(p.binaryPath)
|
||||
cmd := exec.Command(p.binaryPath, p.args...)
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
27
vendor/helm.sh/helm/v3/pkg/provenance/sign.go
vendored
27
vendor/helm.sh/helm/v3/pkg/provenance/sign.go
vendored
@@ -26,9 +26,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"golang.org/x/crypto/openpgp/clearsign"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
"golang.org/x/crypto/openpgp" //nolint
|
||||
"golang.org/x/crypto/openpgp/clearsign" //nolint
|
||||
"golang.org/x/crypto/openpgp/packet" //nolint
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
hapi "helm.sh/helm/v3/pkg/chart"
|
||||
@@ -216,7 +216,7 @@ func (s *Signatory) ClearSign(chartpath string) (string, error) {
|
||||
|
||||
b, err := messageBlock(chartpath)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Sign the buffer
|
||||
@@ -224,9 +224,24 @@ func (s *Signatory) ClearSign(chartpath string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = io.Copy(w, b)
|
||||
w.Close()
|
||||
return out.String(), err
|
||||
|
||||
if err != nil {
|
||||
// NB: We intentionally don't call `w.Close()` here! `w.Close()` is the method which
|
||||
// actually does the PGP signing, and therefore is the part which uses the private key.
|
||||
// In other words, if we call Close here, there's a risk that there's an attempt to use the
|
||||
// private key to sign garbage data (since we know that io.Copy failed, `w` won't contain
|
||||
// anything useful).
|
||||
return "", errors.Wrap(err, "failed to write to clearsign encoder")
|
||||
}
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to either sign or armor message block")
|
||||
}
|
||||
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
// Verify checks a signature and verifies that it is legit for a chart.
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/*
|
||||
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
|
||||
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,
|
||||
@@ -14,15 +13,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/internal/experimental/registry"
|
||||
|
||||
import (
|
||||
"github.com/deislabs/oras/pkg/auth"
|
||||
)
|
||||
|
||||
type (
|
||||
// Authorizer handles registry auth operations
|
||||
Authorizer struct {
|
||||
auth.Client
|
||||
}
|
||||
)
|
||||
/*
|
||||
Package pusher provides a generalized tool for uploading data by scheme.
|
||||
This provides a method by which the plugin system can load arbitrary protocol
|
||||
handlers based upon a URL scheme.
|
||||
*/
|
||||
package pusher
|
||||
104
vendor/helm.sh/helm/v3/pkg/pusher/ocipusher.go
vendored
Normal file
104
vendor/helm.sh/helm/v3/pkg/pusher/ocipusher.go
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package pusher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
)
|
||||
|
||||
// OCIPusher is the default OCI backend handler
|
||||
type OCIPusher struct {
|
||||
opts options
|
||||
}
|
||||
|
||||
// Push performs a Push from repo.Pusher.
|
||||
func (pusher *OCIPusher) Push(chartRef, href string, options ...Option) error {
|
||||
for _, opt := range options {
|
||||
opt(&pusher.opts)
|
||||
}
|
||||
return pusher.push(chartRef, href)
|
||||
}
|
||||
|
||||
func (pusher *OCIPusher) push(chartRef, href string) error {
|
||||
stat, err := os.Stat(chartRef)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return errors.Errorf("%s: no such file", chartRef)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if stat.IsDir() {
|
||||
return errors.New("cannot push directory, must provide chart archive (.tgz)")
|
||||
}
|
||||
|
||||
meta, err := loader.Load(chartRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := pusher.opts.registryClient
|
||||
|
||||
chartBytes, err := ioutil.ReadFile(chartRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pushOpts []registry.PushOption
|
||||
provRef := fmt.Sprintf("%s.prov", chartRef)
|
||||
if _, err := os.Stat(provRef); err == nil {
|
||||
provBytes, err := ioutil.ReadFile(provRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pushOpts = append(pushOpts, registry.PushOptProvData(provBytes))
|
||||
}
|
||||
|
||||
ref := fmt.Sprintf("%s:%s",
|
||||
path.Join(strings.TrimPrefix(href, fmt.Sprintf("%s://", registry.OCIScheme)), meta.Metadata.Name),
|
||||
meta.Metadata.Version)
|
||||
|
||||
_, err = client.Push(chartBytes, ref, pushOpts...)
|
||||
return err
|
||||
}
|
||||
|
||||
// NewOCIPusher constructs a valid OCI client as a Pusher
|
||||
func NewOCIPusher(ops ...Option) (Pusher, error) {
|
||||
registryClient, err := registry.NewClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := OCIPusher{
|
||||
opts: options{
|
||||
registryClient: registryClient,
|
||||
},
|
||||
}
|
||||
|
||||
for _, opt := range ops {
|
||||
opt(&client.opts)
|
||||
}
|
||||
|
||||
return &client, nil
|
||||
}
|
||||
95
vendor/helm.sh/helm/v3/pkg/pusher/pusher.go
vendored
Normal file
95
vendor/helm.sh/helm/v3/pkg/pusher/pusher.go
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package pusher
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
)
|
||||
|
||||
// options are generic parameters to be provided to the pusher during instantiation.
|
||||
//
|
||||
// Pushers may or may not ignore these parameters as they are passed in.
|
||||
type options struct {
|
||||
registryClient *registry.Client
|
||||
}
|
||||
|
||||
// Option allows specifying various settings configurable by the user for overriding the defaults
|
||||
// used when performing Push operations with the Pusher.
|
||||
type Option func(*options)
|
||||
|
||||
// WithRegistryClient sets the registryClient option.
|
||||
func WithRegistryClient(client *registry.Client) Option {
|
||||
return func(opts *options) {
|
||||
opts.registryClient = client
|
||||
}
|
||||
}
|
||||
|
||||
// Pusher is an interface to support upload to the specified URL.
|
||||
type Pusher interface {
|
||||
// Push file content by url string
|
||||
Push(chartRef, url string, options ...Option) error
|
||||
}
|
||||
|
||||
// Constructor is the function for every pusher which creates a specific instance
|
||||
// according to the configuration
|
||||
type Constructor func(options ...Option) (Pusher, error)
|
||||
|
||||
// Provider represents any pusher and the schemes that it supports.
|
||||
type Provider struct {
|
||||
Schemes []string
|
||||
New Constructor
|
||||
}
|
||||
|
||||
// Provides returns true if the given scheme is supported by this Provider.
|
||||
func (p Provider) Provides(scheme string) bool {
|
||||
for _, i := range p.Schemes {
|
||||
if i == scheme {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Providers is a collection of Provider objects.
|
||||
type Providers []Provider
|
||||
|
||||
// ByScheme returns a Provider that handles the given scheme.
|
||||
//
|
||||
// If no provider handles this scheme, this will return an error.
|
||||
func (p Providers) ByScheme(scheme string) (Pusher, error) {
|
||||
for _, pp := range p {
|
||||
if pp.Provides(scheme) {
|
||||
return pp.New()
|
||||
}
|
||||
}
|
||||
return nil, errors.Errorf("scheme %q not supported", scheme)
|
||||
}
|
||||
|
||||
var ociProvider = Provider{
|
||||
Schemes: []string{registry.OCIScheme},
|
||||
New: NewOCIPusher,
|
||||
}
|
||||
|
||||
// All finds all of the registered pushers as a list of Provider instances.
|
||||
// Currently, just the built-in pushers are collected.
|
||||
func All(settings *cli.EnvSettings) Providers {
|
||||
result := Providers{ociProvider}
|
||||
return result
|
||||
}
|
||||
629
vendor/helm.sh/helm/v3/pkg/registry/client.go
vendored
Normal file
629
vendor/helm.sh/helm/v3/pkg/registry/client.go
vendored
Normal file
@@ -0,0 +1,629 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/pkg/registry"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/containerd/containerd/remotes"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"oras.land/oras-go/pkg/auth"
|
||||
dockerauth "oras.land/oras-go/pkg/auth/docker"
|
||||
"oras.land/oras-go/pkg/content"
|
||||
"oras.land/oras-go/pkg/oras"
|
||||
"oras.land/oras-go/pkg/registry"
|
||||
registryremote "oras.land/oras-go/pkg/registry/remote"
|
||||
registryauth "oras.land/oras-go/pkg/registry/remote/auth"
|
||||
|
||||
"helm.sh/helm/v3/internal/version"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/helmpath"
|
||||
)
|
||||
|
||||
// See https://github.com/helm/helm/issues/10166
|
||||
const registryUnderscoreMessage = `
|
||||
OCI artifact references (e.g. tags) do not support the plus sign (+). To support
|
||||
storing semantic versions, Helm adopts the convention of changing plus (+) to
|
||||
an underscore (_) in chart version tags when pushing to a registry and back to
|
||||
a plus (+) when pulling from a registry.`
|
||||
|
||||
type (
|
||||
// Client works with OCI-compliant registries
|
||||
Client struct {
|
||||
debug bool
|
||||
// path to repository config file e.g. ~/.docker/config.json
|
||||
credentialsFile string
|
||||
out io.Writer
|
||||
authorizer auth.Client
|
||||
registryAuthorizer *registryauth.Client
|
||||
resolver remotes.Resolver
|
||||
}
|
||||
|
||||
// ClientOption allows specifying various settings configurable by the user for overriding the defaults
|
||||
// used when creating a new default client
|
||||
ClientOption func(*Client)
|
||||
)
|
||||
|
||||
// NewClient returns a new registry client with config
|
||||
func NewClient(options ...ClientOption) (*Client, error) {
|
||||
client := &Client{
|
||||
out: ioutil.Discard,
|
||||
}
|
||||
for _, option := range options {
|
||||
option(client)
|
||||
}
|
||||
if client.credentialsFile == "" {
|
||||
client.credentialsFile = helmpath.ConfigPath(CredentialsFileBasename)
|
||||
}
|
||||
if client.authorizer == nil {
|
||||
authClient, err := dockerauth.NewClientWithDockerFallback(client.credentialsFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.authorizer = authClient
|
||||
}
|
||||
if client.resolver == nil {
|
||||
headers := http.Header{}
|
||||
headers.Set("User-Agent", version.GetUserAgent())
|
||||
opts := []auth.ResolverOption{auth.WithResolverHeaders(headers)}
|
||||
resolver, err := client.authorizer.ResolverWithOpts(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.resolver = resolver
|
||||
}
|
||||
if client.registryAuthorizer == nil {
|
||||
client.registryAuthorizer = ®istryauth.Client{
|
||||
Header: http.Header{
|
||||
"User-Agent": {version.GetUserAgent()},
|
||||
},
|
||||
Cache: registryauth.DefaultCache,
|
||||
Credential: func(ctx context.Context, reg string) (registryauth.Credential, error) {
|
||||
dockerClient, ok := client.authorizer.(*dockerauth.Client)
|
||||
if !ok {
|
||||
return registryauth.EmptyCredential, errors.New("unable to obtain docker client")
|
||||
}
|
||||
|
||||
username, password, err := dockerClient.Credential(reg)
|
||||
if err != nil {
|
||||
return registryauth.EmptyCredential, errors.New("unable to retrieve credentials")
|
||||
}
|
||||
|
||||
// A blank returned username and password value is a bearer token
|
||||
if username == "" && password != "" {
|
||||
return registryauth.Credential{
|
||||
RefreshToken: password,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return registryauth.Credential{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}, nil
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// ClientOptDebug returns a function that sets the debug setting on client options set
|
||||
func ClientOptDebug(debug bool) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.debug = debug
|
||||
}
|
||||
}
|
||||
|
||||
// ClientOptWriter returns a function that sets the writer setting on client options set
|
||||
func ClientOptWriter(out io.Writer) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.out = out
|
||||
}
|
||||
}
|
||||
|
||||
// ClientOptCredentialsFile returns a function that sets the credentialsFile setting on a client options set
|
||||
func ClientOptCredentialsFile(credentialsFile string) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.credentialsFile = credentialsFile
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
// LoginOption allows specifying various settings on login
|
||||
LoginOption func(*loginOperation)
|
||||
|
||||
loginOperation struct {
|
||||
username string
|
||||
password string
|
||||
insecure bool
|
||||
}
|
||||
)
|
||||
|
||||
// Login logs into a registry
|
||||
func (c *Client) Login(host string, options ...LoginOption) error {
|
||||
operation := &loginOperation{}
|
||||
for _, option := range options {
|
||||
option(operation)
|
||||
}
|
||||
authorizerLoginOpts := []auth.LoginOption{
|
||||
auth.WithLoginContext(ctx(c.out, c.debug)),
|
||||
auth.WithLoginHostname(host),
|
||||
auth.WithLoginUsername(operation.username),
|
||||
auth.WithLoginSecret(operation.password),
|
||||
auth.WithLoginUserAgent(version.GetUserAgent()),
|
||||
}
|
||||
if operation.insecure {
|
||||
authorizerLoginOpts = append(authorizerLoginOpts, auth.WithLoginInsecure())
|
||||
}
|
||||
if err := c.authorizer.LoginWithOpts(authorizerLoginOpts...); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(c.out, "Login Succeeded")
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoginOptBasicAuth returns a function that sets the username/password settings on login
|
||||
func LoginOptBasicAuth(username string, password string) LoginOption {
|
||||
return func(operation *loginOperation) {
|
||||
operation.username = username
|
||||
operation.password = password
|
||||
}
|
||||
}
|
||||
|
||||
// LoginOptInsecure returns a function that sets the insecure setting on login
|
||||
func LoginOptInsecure(insecure bool) LoginOption {
|
||||
return func(operation *loginOperation) {
|
||||
operation.insecure = insecure
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
// LogoutOption allows specifying various settings on logout
|
||||
LogoutOption func(*logoutOperation)
|
||||
|
||||
logoutOperation struct{}
|
||||
)
|
||||
|
||||
// Logout logs out of a registry
|
||||
func (c *Client) Logout(host string, opts ...LogoutOption) error {
|
||||
operation := &logoutOperation{}
|
||||
for _, opt := range opts {
|
||||
opt(operation)
|
||||
}
|
||||
if err := c.authorizer.Logout(ctx(c.out, c.debug), host); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(c.out, "Removing login credentials for %s\n", host)
|
||||
return nil
|
||||
}
|
||||
|
||||
type (
|
||||
// PullOption allows specifying various settings on pull
|
||||
PullOption func(*pullOperation)
|
||||
|
||||
// PullResult is the result returned upon successful pull.
|
||||
PullResult struct {
|
||||
Manifest *descriptorPullSummary `json:"manifest"`
|
||||
Config *descriptorPullSummary `json:"config"`
|
||||
Chart *descriptorPullSummaryWithMeta `json:"chart"`
|
||||
Prov *descriptorPullSummary `json:"prov"`
|
||||
Ref string `json:"ref"`
|
||||
}
|
||||
|
||||
descriptorPullSummary struct {
|
||||
Data []byte `json:"-"`
|
||||
Digest string `json:"digest"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
descriptorPullSummaryWithMeta struct {
|
||||
descriptorPullSummary
|
||||
Meta *chart.Metadata `json:"meta"`
|
||||
}
|
||||
|
||||
pullOperation struct {
|
||||
withChart bool
|
||||
withProv bool
|
||||
ignoreMissingProv bool
|
||||
}
|
||||
)
|
||||
|
||||
// Pull downloads a chart from a registry
|
||||
func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
|
||||
parsedRef, err := parseReference(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
operation := &pullOperation{
|
||||
withChart: true, // By default, always download the chart layer
|
||||
}
|
||||
for _, option := range options {
|
||||
option(operation)
|
||||
}
|
||||
if !operation.withChart && !operation.withProv {
|
||||
return nil, errors.New(
|
||||
"must specify at least one layer to pull (chart/prov)")
|
||||
}
|
||||
memoryStore := content.NewMemory()
|
||||
allowedMediaTypes := []string{
|
||||
ConfigMediaType,
|
||||
}
|
||||
minNumDescriptors := 1 // 1 for the config
|
||||
if operation.withChart {
|
||||
minNumDescriptors++
|
||||
allowedMediaTypes = append(allowedMediaTypes, ChartLayerMediaType, LegacyChartLayerMediaType)
|
||||
}
|
||||
if operation.withProv {
|
||||
if !operation.ignoreMissingProv {
|
||||
minNumDescriptors++
|
||||
}
|
||||
allowedMediaTypes = append(allowedMediaTypes, ProvLayerMediaType)
|
||||
}
|
||||
|
||||
var descriptors, layers []ocispec.Descriptor
|
||||
registryStore := content.Registry{Resolver: c.resolver}
|
||||
|
||||
manifest, err := oras.Copy(ctx(c.out, c.debug), registryStore, parsedRef.String(), memoryStore, "",
|
||||
oras.WithPullEmptyNameAllowed(),
|
||||
oras.WithAllowedMediaTypes(allowedMediaTypes),
|
||||
oras.WithLayerDescriptors(func(l []ocispec.Descriptor) {
|
||||
layers = l
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
descriptors = append(descriptors, manifest)
|
||||
descriptors = append(descriptors, layers...)
|
||||
|
||||
numDescriptors := len(descriptors)
|
||||
if numDescriptors < minNumDescriptors {
|
||||
return nil, fmt.Errorf("manifest does not contain minimum number of descriptors (%d), descriptors found: %d",
|
||||
minNumDescriptors, numDescriptors)
|
||||
}
|
||||
var configDescriptor *ocispec.Descriptor
|
||||
var chartDescriptor *ocispec.Descriptor
|
||||
var provDescriptor *ocispec.Descriptor
|
||||
for _, descriptor := range descriptors {
|
||||
d := descriptor
|
||||
switch d.MediaType {
|
||||
case ConfigMediaType:
|
||||
configDescriptor = &d
|
||||
case ChartLayerMediaType:
|
||||
chartDescriptor = &d
|
||||
case ProvLayerMediaType:
|
||||
provDescriptor = &d
|
||||
case LegacyChartLayerMediaType:
|
||||
chartDescriptor = &d
|
||||
fmt.Fprintf(c.out, "Warning: chart media type %s is deprecated\n", LegacyChartLayerMediaType)
|
||||
}
|
||||
}
|
||||
if configDescriptor == nil {
|
||||
return nil, fmt.Errorf("could not load config with mediatype %s", ConfigMediaType)
|
||||
}
|
||||
if operation.withChart && chartDescriptor == nil {
|
||||
return nil, fmt.Errorf("manifest does not contain a layer with mediatype %s",
|
||||
ChartLayerMediaType)
|
||||
}
|
||||
var provMissing bool
|
||||
if operation.withProv && provDescriptor == nil {
|
||||
if operation.ignoreMissingProv {
|
||||
provMissing = true
|
||||
} else {
|
||||
return nil, fmt.Errorf("manifest does not contain a layer with mediatype %s",
|
||||
ProvLayerMediaType)
|
||||
}
|
||||
}
|
||||
result := &PullResult{
|
||||
Manifest: &descriptorPullSummary{
|
||||
Digest: manifest.Digest.String(),
|
||||
Size: manifest.Size,
|
||||
},
|
||||
Config: &descriptorPullSummary{
|
||||
Digest: configDescriptor.Digest.String(),
|
||||
Size: configDescriptor.Size,
|
||||
},
|
||||
Chart: &descriptorPullSummaryWithMeta{},
|
||||
Prov: &descriptorPullSummary{},
|
||||
Ref: parsedRef.String(),
|
||||
}
|
||||
var getManifestErr error
|
||||
if _, manifestData, ok := memoryStore.Get(manifest); !ok {
|
||||
getManifestErr = errors.Errorf("Unable to retrieve blob with digest %s", manifest.Digest)
|
||||
} else {
|
||||
result.Manifest.Data = manifestData
|
||||
}
|
||||
if getManifestErr != nil {
|
||||
return nil, getManifestErr
|
||||
}
|
||||
var getConfigDescriptorErr error
|
||||
if _, configData, ok := memoryStore.Get(*configDescriptor); !ok {
|
||||
getConfigDescriptorErr = errors.Errorf("Unable to retrieve blob with digest %s", configDescriptor.Digest)
|
||||
} else {
|
||||
result.Config.Data = configData
|
||||
var meta *chart.Metadata
|
||||
if err := json.Unmarshal(configData, &meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Chart.Meta = meta
|
||||
}
|
||||
if getConfigDescriptorErr != nil {
|
||||
return nil, getConfigDescriptorErr
|
||||
}
|
||||
if operation.withChart {
|
||||
var getChartDescriptorErr error
|
||||
if _, chartData, ok := memoryStore.Get(*chartDescriptor); !ok {
|
||||
getChartDescriptorErr = errors.Errorf("Unable to retrieve blob with digest %s", chartDescriptor.Digest)
|
||||
} else {
|
||||
result.Chart.Data = chartData
|
||||
result.Chart.Digest = chartDescriptor.Digest.String()
|
||||
result.Chart.Size = chartDescriptor.Size
|
||||
}
|
||||
if getChartDescriptorErr != nil {
|
||||
return nil, getChartDescriptorErr
|
||||
}
|
||||
}
|
||||
if operation.withProv && !provMissing {
|
||||
var getProvDescriptorErr error
|
||||
if _, provData, ok := memoryStore.Get(*provDescriptor); !ok {
|
||||
getProvDescriptorErr = errors.Errorf("Unable to retrieve blob with digest %s", provDescriptor.Digest)
|
||||
} else {
|
||||
result.Prov.Data = provData
|
||||
result.Prov.Digest = provDescriptor.Digest.String()
|
||||
result.Prov.Size = provDescriptor.Size
|
||||
}
|
||||
if getProvDescriptorErr != nil {
|
||||
return nil, getProvDescriptorErr
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(c.out, "Pulled: %s\n", result.Ref)
|
||||
fmt.Fprintf(c.out, "Digest: %s\n", result.Manifest.Digest)
|
||||
|
||||
if strings.Contains(result.Ref, "_") {
|
||||
fmt.Fprintf(c.out, "%s contains an underscore.\n", result.Ref)
|
||||
fmt.Fprint(c.out, registryUnderscoreMessage+"\n")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PullOptWithChart returns a function that sets the withChart setting on pull
|
||||
func PullOptWithChart(withChart bool) PullOption {
|
||||
return func(operation *pullOperation) {
|
||||
operation.withChart = withChart
|
||||
}
|
||||
}
|
||||
|
||||
// PullOptWithProv returns a function that sets the withProv setting on pull
|
||||
func PullOptWithProv(withProv bool) PullOption {
|
||||
return func(operation *pullOperation) {
|
||||
operation.withProv = withProv
|
||||
}
|
||||
}
|
||||
|
||||
// PullOptIgnoreMissingProv returns a function that sets the ignoreMissingProv setting on pull
|
||||
func PullOptIgnoreMissingProv(ignoreMissingProv bool) PullOption {
|
||||
return func(operation *pullOperation) {
|
||||
operation.ignoreMissingProv = ignoreMissingProv
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
// PushOption allows specifying various settings on push
|
||||
PushOption func(*pushOperation)
|
||||
|
||||
// PushResult is the result returned upon successful push.
|
||||
PushResult struct {
|
||||
Manifest *descriptorPushSummary `json:"manifest"`
|
||||
Config *descriptorPushSummary `json:"config"`
|
||||
Chart *descriptorPushSummaryWithMeta `json:"chart"`
|
||||
Prov *descriptorPushSummary `json:"prov"`
|
||||
Ref string `json:"ref"`
|
||||
}
|
||||
|
||||
descriptorPushSummary struct {
|
||||
Digest string `json:"digest"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
descriptorPushSummaryWithMeta struct {
|
||||
descriptorPushSummary
|
||||
Meta *chart.Metadata `json:"meta"`
|
||||
}
|
||||
|
||||
pushOperation struct {
|
||||
provData []byte
|
||||
strictMode bool
|
||||
}
|
||||
)
|
||||
|
||||
// Push uploads a chart to a registry.
|
||||
func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResult, error) {
|
||||
parsedRef, err := parseReference(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
operation := &pushOperation{
|
||||
strictMode: true, // By default, enable strict mode
|
||||
}
|
||||
for _, option := range options {
|
||||
option(operation)
|
||||
}
|
||||
meta, err := extractChartMeta(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if operation.strictMode {
|
||||
if !strings.HasSuffix(ref, fmt.Sprintf("/%s:%s", meta.Name, meta.Version)) {
|
||||
return nil, errors.New(
|
||||
"strict mode enabled, ref basename and tag must match the chart name and version")
|
||||
}
|
||||
}
|
||||
memoryStore := content.NewMemory()
|
||||
chartDescriptor, err := memoryStore.Add("", ChartLayerMediaType, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configData, err := json.Marshal(meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configDescriptor, err := memoryStore.Add("", ConfigMediaType, configData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
descriptors := []ocispec.Descriptor{chartDescriptor}
|
||||
var provDescriptor ocispec.Descriptor
|
||||
if operation.provData != nil {
|
||||
provDescriptor, err = memoryStore.Add("", ProvLayerMediaType, operation.provData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
descriptors = append(descriptors, provDescriptor)
|
||||
}
|
||||
|
||||
manifestData, manifest, err := content.GenerateManifest(&configDescriptor, nil, descriptors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := memoryStore.StoreManifest(parsedRef.String(), manifest, manifestData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registryStore := content.Registry{Resolver: c.resolver}
|
||||
_, err = oras.Copy(ctx(c.out, c.debug), memoryStore, parsedRef.String(), registryStore, "",
|
||||
oras.WithNameValidation(nil))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chartSummary := &descriptorPushSummaryWithMeta{
|
||||
Meta: meta,
|
||||
}
|
||||
chartSummary.Digest = chartDescriptor.Digest.String()
|
||||
chartSummary.Size = chartDescriptor.Size
|
||||
result := &PushResult{
|
||||
Manifest: &descriptorPushSummary{
|
||||
Digest: manifest.Digest.String(),
|
||||
Size: manifest.Size,
|
||||
},
|
||||
Config: &descriptorPushSummary{
|
||||
Digest: configDescriptor.Digest.String(),
|
||||
Size: configDescriptor.Size,
|
||||
},
|
||||
Chart: chartSummary,
|
||||
Prov: &descriptorPushSummary{}, // prevent nil references
|
||||
Ref: parsedRef.String(),
|
||||
}
|
||||
if operation.provData != nil {
|
||||
result.Prov = &descriptorPushSummary{
|
||||
Digest: provDescriptor.Digest.String(),
|
||||
Size: provDescriptor.Size,
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(c.out, "Pushed: %s\n", result.Ref)
|
||||
fmt.Fprintf(c.out, "Digest: %s\n", result.Manifest.Digest)
|
||||
if strings.Contains(parsedRef.Reference, "_") {
|
||||
fmt.Fprintf(c.out, "%s contains an underscore.\n", result.Ref)
|
||||
fmt.Fprint(c.out, registryUnderscoreMessage+"\n")
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
// PushOptProvData returns a function that sets the prov bytes setting on push
|
||||
func PushOptProvData(provData []byte) PushOption {
|
||||
return func(operation *pushOperation) {
|
||||
operation.provData = provData
|
||||
}
|
||||
}
|
||||
|
||||
// PushOptStrictMode returns a function that sets the strictMode setting on push
|
||||
func PushOptStrictMode(strictMode bool) PushOption {
|
||||
return func(operation *pushOperation) {
|
||||
operation.strictMode = strictMode
|
||||
}
|
||||
}
|
||||
|
||||
// Tags provides a sorted list all semver compliant tags for a given repository
|
||||
func (c *Client) Tags(ref string) ([]string, error) {
|
||||
parsedReference, err := registry.ParseReference(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repository := registryremote.Repository{
|
||||
Reference: parsedReference,
|
||||
Client: c.registryAuthorizer,
|
||||
}
|
||||
|
||||
var registryTags []string
|
||||
|
||||
for {
|
||||
registryTags, err = registry.Tags(ctx(c.out, c.debug), &repository)
|
||||
if err != nil {
|
||||
// Fallback to http based request
|
||||
if !repository.PlainHTTP && strings.Contains(err.Error(), "server gave HTTP response") {
|
||||
repository.PlainHTTP = true
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
var tagVersions []*semver.Version
|
||||
for _, tag := range registryTags {
|
||||
// Change underscore (_) back to plus (+) for Helm
|
||||
// See https://github.com/helm/helm/issues/10166
|
||||
tagVersion, err := semver.StrictNewVersion(strings.ReplaceAll(tag, "_", "+"))
|
||||
if err == nil {
|
||||
tagVersions = append(tagVersions, tagVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the collection
|
||||
sort.Sort(sort.Reverse(semver.Collection(tagVersions)))
|
||||
|
||||
tags := make([]string, len(tagVersions))
|
||||
|
||||
for iTv, tv := range tagVersions {
|
||||
tags[iTv] = tv.String()
|
||||
}
|
||||
|
||||
return tags, nil
|
||||
|
||||
}
|
||||
37
vendor/helm.sh/helm/v3/pkg/registry/constants.go
vendored
Normal file
37
vendor/helm.sh/helm/v3/pkg/registry/constants.go
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/pkg/registry"
|
||||
|
||||
const (
|
||||
// OCIScheme is the URL scheme for OCI-based requests
|
||||
OCIScheme = "oci"
|
||||
|
||||
// CredentialsFileBasename is the filename for auth credentials file
|
||||
CredentialsFileBasename = "registry/config.json"
|
||||
|
||||
// ConfigMediaType is the reserved media type for the Helm chart manifest config
|
||||
ConfigMediaType = "application/vnd.cncf.helm.config.v1+json"
|
||||
|
||||
// ChartLayerMediaType is the reserved media type for Helm chart package content
|
||||
ChartLayerMediaType = "application/vnd.cncf.helm.chart.content.v1.tar+gzip"
|
||||
|
||||
// ProvLayerMediaType is the reserved media type for Helm chart provenance files
|
||||
ProvLayerMediaType = "application/vnd.cncf.helm.chart.provenance.v1.prov"
|
||||
|
||||
// LegacyChartLayerMediaType is the legacy reserved media type for Helm chart package content.
|
||||
LegacyChartLayerMediaType = "application/tar+gzip"
|
||||
)
|
||||
131
vendor/helm.sh/helm/v3/pkg/registry/util.go
vendored
Normal file
131
vendor/helm.sh/helm/v3/pkg/registry/util.go
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package registry // import "helm.sh/helm/v3/pkg/registry"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
orascontext "oras.land/oras-go/pkg/context"
|
||||
"oras.land/oras-go/pkg/registry"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
)
|
||||
|
||||
// IsOCI determines whether or not a URL is to be treated as an OCI URL
|
||||
func IsOCI(url string) bool {
|
||||
return strings.HasPrefix(url, fmt.Sprintf("%s://", OCIScheme))
|
||||
}
|
||||
|
||||
// ContainsTag determines whether a tag is found in a provided list of tags
|
||||
func ContainsTag(tags []string, tag string) bool {
|
||||
for _, t := range tags {
|
||||
if tag == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetTagMatchingVersionOrConstraint(tags []string, versionString string) (string, error) {
|
||||
var constraint *semver.Constraints
|
||||
if versionString == "" {
|
||||
// If string is empty, set wildcard constraint
|
||||
constraint, _ = semver.NewConstraint("*")
|
||||
} else {
|
||||
// when customer input exact version, check whether have exact match
|
||||
// one first
|
||||
for _, v := range tags {
|
||||
if versionString == v {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise set constraint to the string given
|
||||
var err error
|
||||
constraint, err = semver.NewConstraint(versionString)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise try to find the first available version matching the string,
|
||||
// in case it is a constraint
|
||||
for _, v := range tags {
|
||||
test, err := semver.NewVersion(v)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if constraint.Check(test) {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.Errorf("Could not locate a version matching provided version string %s", versionString)
|
||||
}
|
||||
|
||||
// extractChartMeta is used to extract a chart metadata from a byte array
|
||||
func extractChartMeta(chartData []byte) (*chart.Metadata, error) {
|
||||
ch, err := loader.LoadArchive(bytes.NewReader(chartData))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ch.Metadata, nil
|
||||
}
|
||||
|
||||
// ctx retrieves a fresh context.
|
||||
// disable verbose logging coming from ORAS (unless debug is enabled)
|
||||
func ctx(out io.Writer, debug bool) context.Context {
|
||||
if !debug {
|
||||
return orascontext.Background()
|
||||
}
|
||||
ctx := orascontext.WithLoggerFromWriter(context.Background(), out)
|
||||
orascontext.GetLogger(ctx).Logger.SetLevel(logrus.DebugLevel)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// parseReference will parse and validate the reference, and clean tags when
|
||||
// applicable tags are only cleaned when plus (+) signs are present, and are
|
||||
// converted to underscores (_) before pushing
|
||||
// See https://github.com/helm/helm/issues/10166
|
||||
func parseReference(raw string) (registry.Reference, error) {
|
||||
// The sole possible reference modification is replacing plus (+) signs
|
||||
// present in tags with underscores (_). To do this properly, we first
|
||||
// need to identify a tag, and then pass it on to the reference parser
|
||||
// NOTE: Passing immediately to the reference parser will fail since (+)
|
||||
// signs are an invalid tag character, and simply replacing all plus (+)
|
||||
// occurrences could invalidate other portions of the URI
|
||||
parts := strings.Split(raw, ":")
|
||||
if len(parts) > 1 && !strings.Contains(parts[len(parts)-1], "/") {
|
||||
tag := parts[len(parts)-1]
|
||||
|
||||
if tag != "" {
|
||||
// Replace any plus (+) signs with known underscore (_) conversion
|
||||
newTag := strings.ReplaceAll(tag, "+", "_")
|
||||
raw = strings.ReplaceAll(raw, tag, newTag)
|
||||
}
|
||||
}
|
||||
|
||||
return registry.ParseReference(raw)
|
||||
}
|
||||
@@ -61,6 +61,7 @@ var InstallOrder KindSortOrder = []string{
|
||||
"StatefulSet",
|
||||
"Job",
|
||||
"CronJob",
|
||||
"IngressClass",
|
||||
"Ingress",
|
||||
"APIService",
|
||||
}
|
||||
@@ -71,6 +72,7 @@ var InstallOrder KindSortOrder = []string{
|
||||
var UninstallOrder KindSortOrder = []string{
|
||||
"APIService",
|
||||
"Ingress",
|
||||
"IngressClass",
|
||||
"Service",
|
||||
"CronJob",
|
||||
"Job",
|
||||
|
||||
7
vendor/helm.sh/helm/v3/pkg/repo/index.go
vendored
7
vendor/helm.sh/helm/v3/pkg/repo/index.go
vendored
@@ -50,6 +50,8 @@ var (
|
||||
ErrNoChartVersion = errors.New("no chart version found")
|
||||
// ErrNoChartName indicates that a chart with the given name is not found.
|
||||
ErrNoChartName = errors.New("no chart name found")
|
||||
// ErrEmptyIndexYaml indicates that the content of index.yaml is empty.
|
||||
ErrEmptyIndexYaml = errors.New("empty index.yaml file")
|
||||
)
|
||||
|
||||
// ChartVersions is a list of versioned chart references.
|
||||
@@ -326,6 +328,11 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) {
|
||||
// This will fail if API Version is not set (ErrNoAPIVersion) or if the unmarshal fails.
|
||||
func loadIndex(data []byte, source string) (*IndexFile, error) {
|
||||
i := &IndexFile{}
|
||||
|
||||
if len(data) == 0 {
|
||||
return i, ErrEmptyIndexYaml
|
||||
}
|
||||
|
||||
if err := yaml.UnmarshalStrict(data, i); err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ func (s *Storage) Get(name string, version int) (*rspb.Release, error) {
|
||||
}
|
||||
|
||||
// Create creates a new storage entry holding the release. An
|
||||
// error is returned if the storage driver failed to store the
|
||||
// release, or a release with identical an key already exists.
|
||||
// error is returned if the storage driver fails to store the
|
||||
// release, or a release with an identical key already exists.
|
||||
func (s *Storage) Create(rls *rspb.Release) error {
|
||||
s.Log("creating release %q", makeKey(rls.Name, rls.Version))
|
||||
if s.MaxHistory > 0 {
|
||||
@@ -177,7 +177,7 @@ func (s *Storage) removeLeastRecent(name string, max int) error {
|
||||
relutil.SortByRevision(h)
|
||||
|
||||
lastDeployed, err := s.Deployed(name)
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(err, driver.ErrNoDeployedReleases) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
58
vendor/helm.sh/helm/v3/pkg/uploader/chart_uploader.go
vendored
Normal file
58
vendor/helm.sh/helm/v3/pkg/uploader/chart_uploader.go
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package uploader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/pusher"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
)
|
||||
|
||||
// ChartUploader handles uploading a chart.
|
||||
type ChartUploader struct {
|
||||
// Out is the location to write warning and info messages.
|
||||
Out io.Writer
|
||||
// Pusher collection for the operation
|
||||
Pushers pusher.Providers
|
||||
// Options provide parameters to be passed along to the Pusher being initialized.
|
||||
Options []pusher.Option
|
||||
// RegistryClient is a client for interacting with registries.
|
||||
RegistryClient *registry.Client
|
||||
}
|
||||
|
||||
// UploadTo uploads a chart. Depending on the settings, it may also upload a provenance file.
|
||||
func (c *ChartUploader) UploadTo(ref, remote string) error {
|
||||
u, err := url.Parse(remote)
|
||||
if err != nil {
|
||||
return errors.Errorf("invalid chart URL format: %s", remote)
|
||||
}
|
||||
|
||||
if u.Scheme == "" {
|
||||
return fmt.Errorf("scheme prefix missing from remote (e.g. \"%s://\")", registry.OCIScheme)
|
||||
}
|
||||
|
||||
p, err := c.Pushers.ByScheme(u.Scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.Push(ref, u.String(), c.Options...)
|
||||
}
|
||||
@@ -13,8 +13,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*Package gates provides a general tool for working with experimental feature gates.
|
||||
/*Package uploader provides a library for uploading charts.
|
||||
|
||||
This provides convenience methods where the user can determine if certain experimental features are enabled.
|
||||
This package contains tools for uploading charts to registries.
|
||||
*/
|
||||
package gates
|
||||
package uploader
|
||||
Reference in New Issue
Block a user