feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
@@ -18,7 +18,6 @@ package fileutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -28,7 +27,7 @@ import (
|
||||
// AtomicWriteFile atomically (as atomic as os.Rename allows) writes a file to a
|
||||
// disk.
|
||||
func AtomicWriteFile(filename string, reader io.Reader, mode os.FileMode) error {
|
||||
tempFile, err := ioutil.TempFile(filepath.Split(filename))
|
||||
tempFile, err := os.CreateTemp(filepath.Split(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
67
vendor/helm.sh/helm/v3/internal/ignore/doc.go
vendored
67
vendor/helm.sh/helm/v3/internal/ignore/doc.go
vendored
@@ -1,67 +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 ignore provides tools for writing ignore files (a la .gitignore).
|
||||
|
||||
This provides both an ignore parser and a file-aware processor.
|
||||
|
||||
The format of ignore files closely follows, but does not exactly match, the
|
||||
format for .gitignore files (https://git-scm.com/docs/gitignore).
|
||||
|
||||
The formatting rules are as follows:
|
||||
|
||||
- Parsing is line-by-line
|
||||
- Empty lines are ignored
|
||||
- Lines the begin with # (comments) will be ignored
|
||||
- Leading and trailing spaces are always ignored
|
||||
- Inline comments are NOT supported ('foo* # Any foo' does not contain a comment)
|
||||
- There is no support for multi-line patterns
|
||||
- Shell glob patterns are supported. See Go's "path/filepath".Match
|
||||
- If a pattern begins with a leading !, the match will be negated.
|
||||
- If a pattern begins with a leading /, only paths relatively rooted will match.
|
||||
- If the pattern ends with a trailing /, only directories will match
|
||||
- If a pattern contains no slashes, file basenames are tested (not paths)
|
||||
- The pattern sequence "**", while legal in a glob, will cause an error here
|
||||
(to indicate incompatibility with .gitignore).
|
||||
|
||||
Example:
|
||||
|
||||
# Match any file named foo.txt
|
||||
foo.txt
|
||||
|
||||
# Match any text file
|
||||
*.txt
|
||||
|
||||
# Match only directories named mydir
|
||||
mydir/
|
||||
|
||||
# Match only text files in the top-level directory
|
||||
/*.txt
|
||||
|
||||
# Match only the file foo.txt in the top-level directory
|
||||
/foo.txt
|
||||
|
||||
# Match any file named ab.txt, ac.txt, or ad.txt
|
||||
a[b-d].txt
|
||||
|
||||
Notable differences from .gitignore:
|
||||
- The '**' syntax is not supported.
|
||||
- The globbing library is Go's 'filepath.Match', not fnmatch(3)
|
||||
- Trailing spaces are always ignored (there is no supported escape sequence)
|
||||
- The evaluation of escape sequences has not been tested for compatibility
|
||||
- There is no support for '\!' as a special leading sequence.
|
||||
*/
|
||||
package ignore // import "helm.sh/helm/v3/internal/ignore"
|
||||
@@ -92,9 +92,9 @@ func FindNewReplicaSet(deployment *apps.Deployment, rsList []*apps.ReplicaSet) *
|
||||
|
||||
// EqualIgnoreHash returns true if two given podTemplateSpec are equal, ignoring the diff in value of Labels[pod-template-hash]
|
||||
// We ignore pod-template-hash because:
|
||||
// 1. The hash result would be different upon podTemplateSpec API changes
|
||||
// (e.g. the addition of a new field will cause the hash code to change)
|
||||
// 2. The deployment template won't have hash labels
|
||||
// 1. The hash result would be different upon podTemplateSpec API changes
|
||||
// (e.g. the addition of a new field will cause the hash code to change)
|
||||
// 2. The deployment template won't have hash labels
|
||||
func EqualIgnoreHash(template1, template2 *v1.PodTemplateSpec) bool {
|
||||
t1Copy := template1.DeepCopy()
|
||||
t2Copy := template2.DeepCopy()
|
||||
|
||||
10
vendor/helm.sh/helm/v3/internal/tlsutil/tls.go
vendored
10
vendor/helm.sh/helm/v3/internal/tlsutil/tls.go
vendored
@@ -19,14 +19,16 @@ package tlsutil
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// NewClientTLS returns tls.Config appropriate for client auth.
|
||||
func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
|
||||
config := tls.Config{}
|
||||
func NewClientTLS(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*tls.Config, error) {
|
||||
config := tls.Config{
|
||||
InsecureSkipVerify: insecureSkipTLSverify,
|
||||
}
|
||||
|
||||
if certFile != "" && keyFile != "" {
|
||||
cert, err := CertFromFilePair(certFile, keyFile)
|
||||
@@ -52,7 +54,7 @@ func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
|
||||
// Returns an error if the file could not be read, a certificate could not
|
||||
// be parsed, or if the file does not contain any certificates
|
||||
func CertPoolFromFile(filename string) (*x509.CertPool, error) {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
b, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("can't read CA file: %v", filename)
|
||||
}
|
||||
|
||||
@@ -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.11"
|
||||
version = "v3.14"
|
||||
|
||||
// metadata is extra build time data
|
||||
metadata = ""
|
||||
|
||||
18
vendor/helm.sh/helm/v3/pkg/action/action.go
vendored
18
vendor/helm.sh/helm/v3/pkg/action/action.go
vendored
@@ -103,7 +103,7 @@ type Configuration struct {
|
||||
// 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 (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun, enableDNS 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, interactWithRemote, enableDNS bool) ([]*release.Hook, *bytes.Buffer, string, error) {
|
||||
hs := []*release.Hook{}
|
||||
b := bytes.NewBuffer(nil)
|
||||
|
||||
@@ -121,12 +121,10 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
|
||||
var files map[string]string
|
||||
var err2 error
|
||||
|
||||
// A `helm template` or `helm install --dry-run` should not talk to the remote cluster.
|
||||
// It will break in interesting and exotic ways because other data (e.g. discovery)
|
||||
// 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 && cfg.RESTClientGetter != nil {
|
||||
// A `helm template` should not talk to the remote cluster. However, commands with the flag
|
||||
//`--dry-run` with the value of `false`, `none`, or `server` should try to interact with the cluster.
|
||||
// It may break in interesting and exotic ways because other data (e.g. discovery) is mocked.
|
||||
if interactWithRemote && cfg.RESTClientGetter != nil {
|
||||
restConfig, err := cfg.RESTClientGetter.ToRESTConfig()
|
||||
if err != nil {
|
||||
return hs, b, "", err
|
||||
@@ -189,13 +187,13 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
|
||||
if includeCrds {
|
||||
for _, crd := range ch.CRDObjects() {
|
||||
if outputDir == "" {
|
||||
fmt.Fprintf(b, "---\n# Source: %s\n%s\n", crd.Name, string(crd.File.Data[:]))
|
||||
fmt.Fprintf(b, "---\n# Source: %s\n%s\n", crd.Filename, string(crd.File.Data[:]))
|
||||
} else {
|
||||
err = writeToFile(outputDir, crd.Filename, string(crd.File.Data[:]), fileWritten[crd.Name])
|
||||
err = writeToFile(outputDir, crd.Filename, string(crd.File.Data[:]), fileWritten[crd.Filename])
|
||||
if err != nil {
|
||||
return hs, b, "", err
|
||||
}
|
||||
fileWritten[crd.Name] = true
|
||||
fileWritten[crd.Filename] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
69
vendor/helm.sh/helm/v3/pkg/action/get_metadata.go
vendored
Normal file
69
vendor/helm.sh/helm/v3/pkg/action/get_metadata.go
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
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 "time"
|
||||
|
||||
// GetMetadata is the action for checking a given release's metadata.
|
||||
//
|
||||
// It provides the implementation of 'helm get metadata'.
|
||||
type GetMetadata struct {
|
||||
cfg *Configuration
|
||||
|
||||
Version int
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Chart string `json:"chart" yaml:"chart"`
|
||||
Version string `json:"version" yaml:"version"`
|
||||
AppVersion string `json:"appVersion" yaml:"appVersion"`
|
||||
Namespace string `json:"namespace" yaml:"namespace"`
|
||||
Revision int `json:"revision" yaml:"revision"`
|
||||
Status string `json:"status" yaml:"status"`
|
||||
DeployedAt string `json:"deployedAt" yaml:"deployedAt"`
|
||||
}
|
||||
|
||||
// NewGetMetadata creates a new GetMetadata object with the given configuration.
|
||||
func NewGetMetadata(cfg *Configuration) *GetMetadata {
|
||||
return &GetMetadata{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes 'helm get metadata' against the given release.
|
||||
func (g *GetMetadata) Run(name string) (*Metadata, error) {
|
||||
if err := g.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rel, err := g.cfg.releaseContent(name, g.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Metadata{
|
||||
Name: rel.Name,
|
||||
Chart: rel.Chart.Metadata.Name,
|
||||
Version: rel.Chart.Metadata.Version,
|
||||
AppVersion: rel.Chart.Metadata.AppVersion,
|
||||
Namespace: rel.Namespace,
|
||||
Revision: rel.Version,
|
||||
Status: rel.Info.Status.String(),
|
||||
DeployedAt: rel.Info.LastDeployed.Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
16
vendor/helm.sh/helm/v3/pkg/action/hooks.go
vendored
16
vendor/helm.sh/helm/v3/pkg/action/hooks.go
vendored
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
)
|
||||
@@ -51,7 +52,7 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
|
||||
h.DeletePolicies = []release.HookDeletePolicy{release.HookBeforeHookCreation}
|
||||
}
|
||||
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookBeforeHookCreation); err != nil {
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookBeforeHookCreation, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -88,7 +89,7 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
|
||||
h.LastRun.Phase = release.HookPhaseFailed
|
||||
// If a hook is failed, check the annotation of the hook to determine whether the hook should be deleted
|
||||
// under failed condition. If so, then clear the corresponding resource object in the hook
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookFailed); err != nil {
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookFailed, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
@@ -99,7 +100,7 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
|
||||
// If all hooks are successful, check the annotation of each hook to determine whether the hook should be deleted
|
||||
// under succeeded condition. If so, then clear the corresponding resource object in each hook
|
||||
for _, h := range executingHooks {
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookSucceeded); err != nil {
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookSucceeded, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -120,7 +121,7 @@ func (x hookByWeight) Less(i, j int) bool {
|
||||
}
|
||||
|
||||
// deleteHookByPolicy deletes a hook if the hook policy instructs it to
|
||||
func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy) error {
|
||||
func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy, timeout time.Duration) error {
|
||||
// Never delete CustomResourceDefinitions; this could cause lots of
|
||||
// cascading garbage collection.
|
||||
if h.Kind == "CustomResourceDefinition" {
|
||||
@@ -135,6 +136,13 @@ func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.Hoo
|
||||
if len(errs) > 0 {
|
||||
return errors.New(joinErrors(errs))
|
||||
}
|
||||
|
||||
//wait for resources until they are deleted to avoid conflicts
|
||||
if kubeClient, ok := cfg.KubeClient.(kube.InterfaceExt); ok {
|
||||
if err := kubeClient.WaitForDelete(resources, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
187
vendor/helm.sh/helm/v3/pkg/action/install.go
vendored
187
vendor/helm.sh/helm/v3/pkg/action/install.go
vendored
@@ -20,7 +20,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
"sigs.k8s.io/yaml"
|
||||
@@ -72,6 +73,7 @@ type Install struct {
|
||||
Force bool
|
||||
CreateNamespace bool
|
||||
DryRun bool
|
||||
DryRunOption string
|
||||
DisableHooks bool
|
||||
Replace bool
|
||||
Wait bool
|
||||
@@ -90,6 +92,7 @@ type Install struct {
|
||||
SubNotes bool
|
||||
DisableOpenAPIValidation bool
|
||||
IncludeCRDs bool
|
||||
Labels map[string]string
|
||||
// KubeVersion allows specifying a custom kubernetes version to use and
|
||||
// APIVersions allows a manual set of supported API Versions to be passed
|
||||
// (for things like templating). These are ignored if ClientOnly is false
|
||||
@@ -113,6 +116,7 @@ type ChartPathOptions struct {
|
||||
CertFile string // --cert-file
|
||||
KeyFile string // --key-file
|
||||
InsecureSkipTLSverify bool // --insecure-skip-verify
|
||||
PlainHTTP bool // --plain-http
|
||||
Keyring string // --keyring
|
||||
Password string // --password
|
||||
PassCredentialsAll bool // --pass-credentials
|
||||
@@ -136,6 +140,16 @@ func NewInstall(cfg *Configuration) *Install {
|
||||
return in
|
||||
}
|
||||
|
||||
// SetRegistryClient sets the registry client for the install action
|
||||
func (i *Install) SetRegistryClient(registryClient *registry.Client) {
|
||||
i.ChartPathOptions.registryClient = registryClient
|
||||
}
|
||||
|
||||
// GetRegistryClient get the registry client.
|
||||
func (i *Install) GetRegistryClient() *registry.Client {
|
||||
return i.ChartPathOptions.registryClient
|
||||
}
|
||||
|
||||
func (i *Install) installCRDs(crds []chart.CRD) error {
|
||||
// We do these one file at a time in the order they were read.
|
||||
totalItems := []*resource.Info{}
|
||||
@@ -159,22 +173,38 @@ func (i *Install) installCRDs(crds []chart.CRD) error {
|
||||
totalItems = append(totalItems, res...)
|
||||
}
|
||||
if len(totalItems) > 0 {
|
||||
// Invalidate the local cache, since it will not have the new CRDs
|
||||
// present.
|
||||
discoveryClient, err := i.cfg.RESTClientGetter.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.cfg.Log("Clearing discovery cache")
|
||||
discoveryClient.Invalidate()
|
||||
// Give time for the CRD to be recognized.
|
||||
|
||||
if err := i.cfg.KubeClient.Wait(totalItems, 60*time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure to force a rebuild of the cache.
|
||||
discoveryClient.ServerGroups()
|
||||
// If we have already gathered the capabilities, we need to invalidate
|
||||
// the cache so that the new CRDs are recognized. This should only be
|
||||
// the case when an action configuration is reused for multiple actions,
|
||||
// as otherwise it is later loaded by ourselves when getCapabilities
|
||||
// is called later on in the installation process.
|
||||
if i.cfg.Capabilities != nil {
|
||||
discoveryClient, err := i.cfg.RESTClientGetter.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.cfg.Log("Clearing discovery cache")
|
||||
discoveryClient.Invalidate()
|
||||
|
||||
_, _ = discoveryClient.ServerGroups()
|
||||
}
|
||||
|
||||
// Invalidate the REST mapper, since it will not have the new CRDs
|
||||
// present.
|
||||
restMapper, err := i.cfg.RESTClientGetter.ToRESTMapper()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resettable, ok := restMapper.(meta.ResettableRESTMapper); ok {
|
||||
i.cfg.Log("Clearing REST mapper cache")
|
||||
resettable.Reset()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -189,6 +219,9 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
|
||||
}
|
||||
|
||||
// Run executes the installation with Context
|
||||
//
|
||||
// When the task is cancelled through ctx, the function returns and the install
|
||||
// proceeds in the background.
|
||||
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 {
|
||||
@@ -201,15 +234,20 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := chartutil.ProcessDependencies(chrt, vals); err != nil {
|
||||
if err := chartutil.ProcessDependenciesWithMerge(chrt, vals); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var interactWithRemote bool
|
||||
if !i.isDryRun() || i.DryRunOption == "server" || i.DryRunOption == "none" || i.DryRunOption == "false" {
|
||||
interactWithRemote = true
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// On dry run, bail here
|
||||
if i.DryRun {
|
||||
if i.isDryRun() {
|
||||
i.cfg.Log("WARNING: This chart or one of its subcharts contains CRDs. Rendering may fail or contain inaccuracies.")
|
||||
} else if err := i.installCRDs(crds); err != nil {
|
||||
return nil, err
|
||||
@@ -224,7 +262,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
||||
i.cfg.Capabilities.KubeVersion = *i.KubeVersion
|
||||
}
|
||||
i.cfg.Capabilities.APIVersions = append(i.cfg.Capabilities.APIVersions, i.APIVersions...)
|
||||
i.cfg.KubeClient = &kubefake.PrintingKubeClient{Out: ioutil.Discard}
|
||||
i.cfg.KubeClient = &kubefake.PrintingKubeClient{Out: io.Discard}
|
||||
|
||||
mem := driver.NewMemory()
|
||||
mem.SetNamespace(i.Namespace)
|
||||
@@ -243,7 +281,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
||||
}
|
||||
|
||||
// special case for helm template --is-upgrade
|
||||
isUpgrade := i.IsUpgrade && i.DryRun
|
||||
isUpgrade := i.IsUpgrade && i.isDryRun()
|
||||
options := chartutil.ReleaseOptions{
|
||||
Name: i.ReleaseName,
|
||||
Namespace: i.Namespace,
|
||||
@@ -256,10 +294,14 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rel := i.createRelease(chrt, vals)
|
||||
if driver.ContainsSystemLabels(i.Labels) {
|
||||
return nil, fmt.Errorf("user suplied labels contains system reserved label name. System labels: %+v", driver.GetSystemLabels())
|
||||
}
|
||||
|
||||
rel := i.createRelease(chrt, vals, i.Labels)
|
||||
|
||||
var manifestDoc *bytes.Buffer
|
||||
rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun, i.EnableDNS)
|
||||
rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, interactWithRemote, i.EnableDNS)
|
||||
// Even for errors, attach this if available
|
||||
if manifestDoc != nil {
|
||||
rel.Manifest = manifestDoc.String()
|
||||
@@ -295,12 +337,12 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
||||
if !i.ClientOnly && !isUpgrade && len(resources) > 0 {
|
||||
toBeAdopted, err = existingResourceConflict(resources, rel.Name, rel.Namespace)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with install")
|
||||
return nil, errors.Wrap(err, "Unable to continue with install")
|
||||
}
|
||||
}
|
||||
|
||||
// Bail out here if it is a dry run
|
||||
if i.DryRun {
|
||||
if i.isDryRun() {
|
||||
rel.Info.Description = "Dry run complete"
|
||||
return rel, nil
|
||||
}
|
||||
@@ -346,23 +388,48 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
||||
// 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
|
||||
|
||||
rel, err = i.performInstallCtx(ctx, rel, toBeAdopted, resources)
|
||||
if err != nil {
|
||||
rel, err = i.failRelease(rel, err)
|
||||
}
|
||||
return rel, err
|
||||
}
|
||||
|
||||
func (i *Install) performInstall(c chan<- resultMessage, rel *release.Release, toBeAdopted kube.ResourceList, resources kube.ResourceList) {
|
||||
func (i *Install) performInstallCtx(ctx context.Context, rel *release.Release, toBeAdopted kube.ResourceList, resources kube.ResourceList) (*release.Release, error) {
|
||||
type Msg struct {
|
||||
r *release.Release
|
||||
e error
|
||||
}
|
||||
resultChan := make(chan Msg, 1)
|
||||
|
||||
go func() {
|
||||
rel, err := i.performInstall(rel, toBeAdopted, resources)
|
||||
resultChan <- Msg{rel, err}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err := ctx.Err()
|
||||
return rel, err
|
||||
case msg := <-resultChan:
|
||||
return msg.r, msg.e
|
||||
}
|
||||
}
|
||||
|
||||
// isDryRun returns true if Upgrade is set to run as a DryRun
|
||||
func (i *Install) isDryRun() bool {
|
||||
if i.DryRun || i.DryRunOption == "client" || i.DryRunOption == "server" || i.DryRunOption == "true" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.ResourceList, resources kube.ResourceList) (*release.Release, error) {
|
||||
var err error
|
||||
// pre-install hooks
|
||||
if !i.DisableHooks {
|
||||
if err := i.cfg.execHook(rel, release.HookPreInstall, i.Timeout); err != nil {
|
||||
i.reportToRun(c, rel, fmt.Errorf("failed pre-install: %s", err))
|
||||
return
|
||||
return rel, fmt.Errorf("failed pre-install: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,35 +437,28 @@ func (i *Install) performInstall(c chan<- resultMessage, rel *release.Release, t
|
||||
// do an update, but it's not clear whether we WANT to do an update if the re-use is set
|
||||
// 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 {
|
||||
i.reportToRun(c, rel, err)
|
||||
return
|
||||
}
|
||||
_, err = i.cfg.KubeClient.Create(resources)
|
||||
} else if len(resources) > 0 {
|
||||
if _, err := i.cfg.KubeClient.Update(toBeAdopted, resources, i.Force); err != nil {
|
||||
i.reportToRun(c, rel, err)
|
||||
return
|
||||
}
|
||||
_, err = i.cfg.KubeClient.Update(toBeAdopted, resources, i.Force)
|
||||
}
|
||||
if err != nil {
|
||||
return rel, err
|
||||
}
|
||||
|
||||
if i.Wait {
|
||||
if i.WaitForJobs {
|
||||
if err := i.cfg.KubeClient.WaitWithJobs(resources, i.Timeout); err != nil {
|
||||
i.reportToRun(c, rel, err)
|
||||
return
|
||||
}
|
||||
err = i.cfg.KubeClient.WaitWithJobs(resources, i.Timeout)
|
||||
} else {
|
||||
if err := i.cfg.KubeClient.Wait(resources, i.Timeout); err != nil {
|
||||
i.reportToRun(c, rel, err)
|
||||
return
|
||||
}
|
||||
err = i.cfg.KubeClient.Wait(resources, i.Timeout)
|
||||
}
|
||||
if err != nil {
|
||||
return rel, err
|
||||
}
|
||||
}
|
||||
|
||||
if !i.DisableHooks {
|
||||
if err := i.cfg.execHook(rel, release.HookPostInstall, i.Timeout); err != nil {
|
||||
i.reportToRun(c, rel, fmt.Errorf("failed post-install: %s", err))
|
||||
return
|
||||
return rel, fmt.Errorf("failed post-install: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,25 +479,9 @@ func (i *Install) performInstall(c chan<- resultMessage, rel *release.Release, t
|
||||
i.cfg.Log("failed to record the release: %s", err)
|
||||
}
|
||||
|
||||
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()
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -469,7 +513,8 @@ func (i *Install) availableName() error {
|
||||
if err := chartutil.ValidateReleaseName(start); err != nil {
|
||||
return errors.Wrapf(err, "release name %q", start)
|
||||
}
|
||||
if i.DryRun {
|
||||
// On dry run, bail here
|
||||
if i.isDryRun() {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -487,7 +532,7 @@ func (i *Install) availableName() error {
|
||||
}
|
||||
|
||||
// createRelease creates a new release object
|
||||
func (i *Install) createRelease(chrt *chart.Chart, rawVals map[string]interface{}) *release.Release {
|
||||
func (i *Install) createRelease(chrt *chart.Chart, rawVals map[string]interface{}, labels map[string]string) *release.Release {
|
||||
ts := i.cfg.Now()
|
||||
return &release.Release{
|
||||
Name: i.ReleaseName,
|
||||
@@ -500,6 +545,7 @@ func (i *Install) createRelease(chrt *chart.Chart, rawVals map[string]interface{
|
||||
Status: release.StatusUnknown,
|
||||
},
|
||||
Version: 1,
|
||||
Labels: labels,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,8 +722,6 @@ 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)
|
||||
}
|
||||
@@ -709,6 +753,7 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
|
||||
getter.WithPassCredentialsAll(c.PassCredentialsAll),
|
||||
getter.WithTLSClientConfig(c.CertFile, c.KeyFile, c.CaFile),
|
||||
getter.WithInsecureSkipVerifyTLS(c.InsecureSkipTLSverify),
|
||||
getter.WithPlainHTTP(c.PlainHTTP),
|
||||
},
|
||||
RepositoryConfig: settings.RepositoryConfig,
|
||||
RepositoryCache: settings.RepositoryCache,
|
||||
|
||||
12
vendor/helm.sh/helm/v3/pkg/action/lint.go
vendored
12
vendor/helm.sh/helm/v3/pkg/action/lint.go
vendored
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package action
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -37,6 +36,7 @@ type Lint struct {
|
||||
Namespace string
|
||||
WithSubcharts bool
|
||||
Quiet bool
|
||||
KubeVersion *chartutil.KubeVersion
|
||||
}
|
||||
|
||||
// LintResult is the result of Lint
|
||||
@@ -59,7 +59,7 @@ func (l *Lint) Run(paths []string, vals map[string]interface{}) *LintResult {
|
||||
}
|
||||
result := &LintResult{}
|
||||
for _, path := range paths {
|
||||
linter, err := lintChart(path, vals, l.Namespace, l.Strict)
|
||||
linter, err := lintChart(path, vals, l.Namespace, l.KubeVersion)
|
||||
if err != nil {
|
||||
result.Errors = append(result.Errors, err)
|
||||
continue
|
||||
@@ -83,15 +83,15 @@ func HasWarningsOrErrors(result *LintResult) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return len(result.Errors) > 0
|
||||
}
|
||||
|
||||
func lintChart(path string, vals map[string]interface{}, namespace string, strict bool) (support.Linter, error) {
|
||||
func lintChart(path string, vals map[string]interface{}, namespace string, kubeVersion *chartutil.KubeVersion) (support.Linter, error) {
|
||||
var chartPath string
|
||||
linter := support.Linter{}
|
||||
|
||||
if strings.HasSuffix(path, ".tgz") || strings.HasSuffix(path, ".tar.gz") {
|
||||
tempDir, err := ioutil.TempDir("", "helm-lint")
|
||||
tempDir, err := os.MkdirTemp("", "helm-lint")
|
||||
if err != nil {
|
||||
return linter, errors.Wrap(err, "unable to create temp dir to extract tarball")
|
||||
}
|
||||
@@ -125,5 +125,5 @@ func lintChart(path string, vals map[string]interface{}, namespace string, stric
|
||||
return linter, errors.Wrap(err, "unable to check Chart.yaml file in chart")
|
||||
}
|
||||
|
||||
return lint.All(chartPath, vals, namespace, strict), nil
|
||||
return lint.AllWithKubeVersion(chartPath, vals, namespace, kubeVersion), nil
|
||||
}
|
||||
|
||||
5
vendor/helm.sh/helm/v3/pkg/action/package.go
vendored
5
vendor/helm.sh/helm/v3/pkg/action/package.go
vendored
@@ -19,7 +19,6 @@ package action
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
@@ -55,7 +54,7 @@ func NewPackage() *Package {
|
||||
}
|
||||
|
||||
// Run executes 'helm package' against the given chart and returns the path to the packaged chart.
|
||||
func (p *Package) Run(path string, vals map[string]interface{}) (string, error) {
|
||||
func (p *Package) Run(path string, _ map[string]interface{}) (string, error) {
|
||||
ch, err := loader.LoadDir(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -137,7 +136,7 @@ func (p *Package) Clearsign(filename string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filename+".prov", []byte(sig), 0644)
|
||||
return os.WriteFile(filename+".prov", []byte(sig), 0644)
|
||||
}
|
||||
|
||||
// promptUser implements provenance.PassphraseFetcher
|
||||
|
||||
10
vendor/helm.sh/helm/v3/pkg/action/pull.go
vendored
10
vendor/helm.sh/helm/v3/pkg/action/pull.go
vendored
@@ -18,7 +18,6 @@ package action
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -72,6 +71,11 @@ func NewPullWithOpts(opts ...PullOpt) *Pull {
|
||||
return p
|
||||
}
|
||||
|
||||
// SetRegistryClient sets the registry client on the pull configuration object.
|
||||
func (p *Pull) SetRegistryClient(client *registry.Client) {
|
||||
p.cfg.RegistryClient = client
|
||||
}
|
||||
|
||||
// Run executes 'helm pull' against the given release.
|
||||
func (p *Pull) Run(chartRef string) (string, error) {
|
||||
var out strings.Builder
|
||||
@@ -86,6 +90,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
|
||||
getter.WithPassCredentialsAll(p.PassCredentialsAll),
|
||||
getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile),
|
||||
getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify),
|
||||
getter.WithPlainHTTP(p.PlainHTTP),
|
||||
},
|
||||
RegistryClient: p.cfg.RegistryClient,
|
||||
RepositoryConfig: p.Settings.RepositoryConfig,
|
||||
@@ -95,6 +100,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
|
||||
if registry.IsOCI(chartRef) {
|
||||
c.Options = append(c.Options,
|
||||
getter.WithRegistryClient(p.cfg.RegistryClient))
|
||||
c.RegistryClient = p.cfg.RegistryClient
|
||||
}
|
||||
|
||||
if p.Verify {
|
||||
@@ -108,7 +114,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
|
||||
dest := p.DestDir
|
||||
if p.Untar {
|
||||
var err error
|
||||
dest, err = ioutil.TempDir("", "helm-")
|
||||
dest, err = os.MkdirTemp("", "helm-")
|
||||
if err != nil {
|
||||
return out.String(), errors.Wrap(err, "failed to untar")
|
||||
}
|
||||
|
||||
48
vendor/helm.sh/helm/v3/pkg/action/push.go
vendored
48
vendor/helm.sh/helm/v3/pkg/action/push.go
vendored
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
@@ -29,8 +30,14 @@ import (
|
||||
//
|
||||
// It provides the implementation of 'helm push'.
|
||||
type Push struct {
|
||||
Settings *cli.EnvSettings
|
||||
cfg *Configuration
|
||||
Settings *cli.EnvSettings
|
||||
cfg *Configuration
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
insecureSkipTLSverify bool
|
||||
plainHTTP bool
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
// PushOpt is a type of function that sets options for a push action.
|
||||
@@ -43,6 +50,36 @@ func WithPushConfig(cfg *Configuration) PushOpt {
|
||||
}
|
||||
}
|
||||
|
||||
// WithTLSClientConfig sets the certFile, keyFile, and caFile fields on the push configuration object.
|
||||
func WithTLSClientConfig(certFile, keyFile, caFile string) PushOpt {
|
||||
return func(p *Push) {
|
||||
p.certFile = certFile
|
||||
p.keyFile = keyFile
|
||||
p.caFile = caFile
|
||||
}
|
||||
}
|
||||
|
||||
// WithInsecureSkipTLSVerify determines if a TLS Certificate will be checked
|
||||
func WithInsecureSkipTLSVerify(insecureSkipTLSVerify bool) PushOpt {
|
||||
return func(p *Push) {
|
||||
p.insecureSkipTLSverify = insecureSkipTLSVerify
|
||||
}
|
||||
}
|
||||
|
||||
// WithPlainHTTP configures the use of plain HTTP connections.
|
||||
func WithPlainHTTP(plainHTTP bool) PushOpt {
|
||||
return func(p *Push) {
|
||||
p.plainHTTP = plainHTTP
|
||||
}
|
||||
}
|
||||
|
||||
// WithOptWriter sets the registryOut field on the push configuration object.
|
||||
func WithPushOptWriter(out io.Writer) PushOpt {
|
||||
return func(p *Push) {
|
||||
p.out = out
|
||||
}
|
||||
}
|
||||
|
||||
// NewPushWithOpts creates a new push, with configuration options.
|
||||
func NewPushWithOpts(opts ...PushOpt) *Push {
|
||||
p := &Push{}
|
||||
@@ -59,10 +96,15 @@ func (p *Push) Run(chartRef string, remote string) (string, error) {
|
||||
c := uploader.ChartUploader{
|
||||
Out: &out,
|
||||
Pushers: pusher.All(p.Settings),
|
||||
Options: []pusher.Option{},
|
||||
Options: []pusher.Option{
|
||||
pusher.WithTLSClientConfig(p.certFile, p.keyFile, p.caFile),
|
||||
pusher.WithInsecureSkipTLSVerify(p.insecureSkipTLSverify),
|
||||
pusher.WithPlainHTTP(p.plainHTTP),
|
||||
},
|
||||
}
|
||||
|
||||
if registry.IsOCI(remote) {
|
||||
// Don't use the default registry client if tls options are set.
|
||||
c.Options = append(c.Options, pusher.WithRegistryClient(p.cfg.RegistryClient))
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,45 @@ import (
|
||||
|
||||
// RegistryLogin performs a registry login operation.
|
||||
type RegistryLogin struct {
|
||||
cfg *Configuration
|
||||
cfg *Configuration
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
insecure bool
|
||||
}
|
||||
|
||||
type RegistryLoginOpt func(*RegistryLogin) error
|
||||
|
||||
// WithCertFile specifies the path to the certificate file to use for TLS.
|
||||
func WithCertFile(certFile string) RegistryLoginOpt {
|
||||
return func(r *RegistryLogin) error {
|
||||
r.certFile = certFile
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithKeyFile specifies whether to very certificates when communicating.
|
||||
func WithInsecure(insecure bool) RegistryLoginOpt {
|
||||
return func(r *RegistryLogin) error {
|
||||
r.insecure = insecure
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithKeyFile specifies the path to the key file to use for TLS.
|
||||
func WithKeyFile(keyFile string) RegistryLoginOpt {
|
||||
return func(r *RegistryLogin) error {
|
||||
r.keyFile = keyFile
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithCAFile specifies the path to the CA file to use for TLS.
|
||||
func WithCAFile(caFile string) RegistryLoginOpt {
|
||||
return func(r *RegistryLogin) error {
|
||||
r.caFile = caFile
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewRegistryLogin creates a new RegistryLogin object with the given configuration.
|
||||
@@ -35,9 +73,16 @@ 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 {
|
||||
func (a *RegistryLogin) Run(_ io.Writer, hostname string, username string, password string, opts ...RegistryLoginOpt) error {
|
||||
for _, opt := range opts {
|
||||
if err := opt(a); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return a.cfg.RegistryClient.Login(
|
||||
hostname,
|
||||
registry.LoginOptBasicAuth(username, password),
|
||||
registry.LoginOptInsecure(insecure))
|
||||
registry.LoginOptInsecure(a.insecure),
|
||||
registry.LoginOptTLSClientConfig(a.certFile, a.keyFile, a.caFile))
|
||||
}
|
||||
|
||||
@@ -33,6 +33,6 @@ func NewRegistryLogout(cfg *Configuration) *RegistryLogout {
|
||||
}
|
||||
|
||||
// Run executes the registry logout operation
|
||||
func (a *RegistryLogout) Run(out io.Writer, hostname string) error {
|
||||
func (a *RegistryLogout) Run(_ io.Writer, hostname string) error {
|
||||
return a.cfg.RegistryClient.Logout(hostname)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -29,6 +30,11 @@ import (
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
const (
|
||||
ExcludeNameFilter = "!name"
|
||||
IncludeNameFilter = "name"
|
||||
)
|
||||
|
||||
// ReleaseTesting is the action for testing a release.
|
||||
//
|
||||
// It provides the implementation of 'helm test'.
|
||||
@@ -66,9 +72,9 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) {
|
||||
|
||||
skippedHooks := []*release.Hook{}
|
||||
executingHooks := []*release.Hook{}
|
||||
if len(r.Filters["!name"]) != 0 {
|
||||
if len(r.Filters[ExcludeNameFilter]) != 0 {
|
||||
for _, h := range rel.Hooks {
|
||||
if contains(r.Filters["!name"], h.Name) {
|
||||
if contains(r.Filters[ExcludeNameFilter], h.Name) {
|
||||
skippedHooks = append(skippedHooks, h)
|
||||
} else {
|
||||
executingHooks = append(executingHooks, h)
|
||||
@@ -76,10 +82,10 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) {
|
||||
}
|
||||
rel.Hooks = executingHooks
|
||||
}
|
||||
if len(r.Filters["name"]) != 0 {
|
||||
if len(r.Filters[IncludeNameFilter]) != 0 {
|
||||
executingHooks = nil
|
||||
for _, h := range rel.Hooks {
|
||||
if contains(r.Filters["name"], h.Name) {
|
||||
if contains(r.Filters[IncludeNameFilter], h.Name) {
|
||||
executingHooks = append(executingHooks, h)
|
||||
} else {
|
||||
skippedHooks = append(skippedHooks, h)
|
||||
@@ -107,9 +113,17 @@ func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error {
|
||||
return errors.Wrap(err, "unable to get kubernetes client to fetch pod logs")
|
||||
}
|
||||
|
||||
for _, h := range rel.Hooks {
|
||||
hooksByWight := append([]*release.Hook{}, rel.Hooks...)
|
||||
sort.Stable(hookByWeight(hooksByWight))
|
||||
for _, h := range hooksByWight {
|
||||
for _, e := range h.Events {
|
||||
if e == release.HookTest {
|
||||
if contains(r.Filters[ExcludeNameFilter], h.Name) {
|
||||
continue
|
||||
}
|
||||
if len(r.Filters[IncludeNameFilter]) > 0 && !contains(r.Filters[IncludeNameFilter], h.Name) {
|
||||
continue
|
||||
}
|
||||
req := client.CoreV1().Pods(r.Namespace).GetLogs(h.Name, &v1.PodLogOptions{})
|
||||
logReader, err := req.Stream(context.Background())
|
||||
if err != nil {
|
||||
|
||||
19
vendor/helm.sh/helm/v3/pkg/action/rollback.go
vendored
19
vendor/helm.sh/helm/v3/pkg/action/rollback.go
vendored
@@ -110,6 +110,24 @@ func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Rele
|
||||
previousVersion = currentRelease.Version - 1
|
||||
}
|
||||
|
||||
historyReleases, err := r.cfg.Releases.History(name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Check if the history version to be rolled back exists
|
||||
previousVersionExist := false
|
||||
for _, historyRelease := range historyReleases {
|
||||
version := historyRelease.Version
|
||||
if previousVersion == version {
|
||||
previousVersionExist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !previousVersionExist {
|
||||
return nil, nil, errors.Errorf("release has no %d version", previousVersion)
|
||||
}
|
||||
|
||||
r.cfg.Log("rolling back %s (current: v%d, target: v%d)", name, currentRelease.Version, previousVersion)
|
||||
|
||||
previousRelease, err := r.cfg.Releases.Get(name, previousVersion)
|
||||
@@ -133,6 +151,7 @@ func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Rele
|
||||
Description: fmt.Sprintf("Rollback to %d", previousVersion),
|
||||
},
|
||||
Version: currentRelease.Version + 1,
|
||||
Labels: previousRelease.Labels,
|
||||
Manifest: previousRelease.Manifest,
|
||||
Hooks: previousRelease.Hooks,
|
||||
}
|
||||
|
||||
9
vendor/helm.sh/helm/v3/pkg/action/show.go
vendored
9
vendor/helm.sh/helm/v3/pkg/action/show.go
vendored
@@ -28,6 +28,7 @@ import (
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
)
|
||||
|
||||
// ShowOutputFormat is the format of the output of `helm show`
|
||||
@@ -82,6 +83,11 @@ func NewShowWithConfig(output ShowOutputFormat, cfg *Configuration) *Show {
|
||||
return sh
|
||||
}
|
||||
|
||||
// SetRegistryClient sets the registry client to use when pulling a chart from a registry.
|
||||
func (s *Show) SetRegistryClient(client *registry.Client) {
|
||||
s.ChartPathOptions.registryClient = client
|
||||
}
|
||||
|
||||
// Run executes 'helm show' against the given release.
|
||||
func (s *Show) Run(chartpath string) (string, error) {
|
||||
if s.chart == nil {
|
||||
@@ -147,6 +153,9 @@ func (s *Show) Run(chartpath string) (string, error) {
|
||||
func findReadme(files []*chart.File) (file *chart.File) {
|
||||
for _, file := range files {
|
||||
for _, n := range readmeFileNames {
|
||||
if file == nil {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(file.Name, n) {
|
||||
return file
|
||||
}
|
||||
|
||||
37
vendor/helm.sh/helm/v3/pkg/action/uninstall.go
vendored
37
vendor/helm.sh/helm/v3/pkg/action/uninstall.go
vendored
@@ -22,6 +22,8 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
@@ -35,12 +37,14 @@ import (
|
||||
type Uninstall struct {
|
||||
cfg *Configuration
|
||||
|
||||
DisableHooks bool
|
||||
DryRun bool
|
||||
KeepHistory bool
|
||||
Wait bool
|
||||
Timeout time.Duration
|
||||
Description string
|
||||
DisableHooks bool
|
||||
DryRun bool
|
||||
IgnoreNotFound bool
|
||||
KeepHistory bool
|
||||
Wait bool
|
||||
DeletionPropagation string
|
||||
Timeout time.Duration
|
||||
Description string
|
||||
}
|
||||
|
||||
// NewUninstall creates a new Uninstall object with the given configuration.
|
||||
@@ -71,6 +75,9 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
|
||||
|
||||
rels, err := u.cfg.Releases.History(name)
|
||||
if err != nil {
|
||||
if u.IgnoreNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Wrapf(err, "uninstall: Release not loaded: %s", name)
|
||||
}
|
||||
if len(rels) < 1 {
|
||||
@@ -220,7 +227,25 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (kube.ResourceList, stri
|
||||
return nil, "", []error{errors.Wrap(err, "unable to build kubernetes objects for delete")}
|
||||
}
|
||||
if len(resources) > 0 {
|
||||
if kubeClient, ok := u.cfg.KubeClient.(kube.InterfaceDeletionPropagation); ok {
|
||||
_, errs = kubeClient.DeleteWithPropagationPolicy(resources, parseCascadingFlag(u.cfg, u.DeletionPropagation))
|
||||
return resources, kept, errs
|
||||
}
|
||||
_, errs = u.cfg.KubeClient.Delete(resources)
|
||||
}
|
||||
return resources, kept, errs
|
||||
}
|
||||
|
||||
func parseCascadingFlag(cfg *Configuration, cascadingFlag string) v1.DeletionPropagation {
|
||||
switch cascadingFlag {
|
||||
case "orphan":
|
||||
return v1.DeletePropagationOrphan
|
||||
case "foreground":
|
||||
return v1.DeletePropagationForeground
|
||||
case "background":
|
||||
return v1.DeletePropagationBackground
|
||||
default:
|
||||
cfg.Log("uninstall: given cascade value: %s, defaulting to delete propagation background", cascadingFlag)
|
||||
return v1.DeletePropagationBackground
|
||||
}
|
||||
}
|
||||
|
||||
63
vendor/helm.sh/helm/v3/pkg/action/upgrade.go
vendored
63
vendor/helm.sh/helm/v3/pkg/action/upgrade.go
vendored
@@ -32,6 +32,7 @@ import (
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"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/driver"
|
||||
@@ -70,8 +71,9 @@ type Upgrade struct {
|
||||
// DisableHooks disables hook processing if set to true.
|
||||
DisableHooks bool
|
||||
// DryRun controls whether the operation is prepared, but not executed.
|
||||
// If `true`, the upgrade is prepared but not performed.
|
||||
DryRun bool
|
||||
// DryRunOption controls whether the operation is prepared, but not executed with options on whether or not to interact with the remote cluster.
|
||||
DryRunOption string
|
||||
// Force will, if set to `true`, ignore certain warnings and perform the upgrade anyway.
|
||||
//
|
||||
// This should be used with caution.
|
||||
@@ -80,6 +82,8 @@ type Upgrade struct {
|
||||
ResetValues bool
|
||||
// ReuseValues will re-use the user's last supplied values.
|
||||
ReuseValues bool
|
||||
// ResetThenReuseValues will reset the values to the chart's built-ins then merge with user's last supplied values.
|
||||
ResetThenReuseValues bool
|
||||
// Recreate will (if true) recreate pods after a rollback.
|
||||
Recreate bool
|
||||
// MaxHistory limits the maximum number of revisions saved per release
|
||||
@@ -92,6 +96,7 @@ type Upgrade struct {
|
||||
SubNotes bool
|
||||
// Description is the description of this operation
|
||||
Description string
|
||||
Labels map[string]string
|
||||
// PostRender is an optional post-renderer
|
||||
//
|
||||
// If this is non-nil, then after templates are rendered, they will be sent to the
|
||||
@@ -122,6 +127,11 @@ func NewUpgrade(cfg *Configuration) *Upgrade {
|
||||
return up
|
||||
}
|
||||
|
||||
// SetRegistryClient sets the registry client to use when fetching charts.
|
||||
func (u *Upgrade) SetRegistryClient(client *registry.Client) {
|
||||
u.ChartPathOptions.registryClient = client
|
||||
}
|
||||
|
||||
// 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()
|
||||
@@ -141,6 +151,7 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
|
||||
if err := chartutil.ValidateReleaseName(name); err != nil {
|
||||
return nil, errors.Errorf("release name is invalid: %s", name)
|
||||
}
|
||||
|
||||
u.cfg.Log("preparing upgrade for %s", name)
|
||||
currentRelease, upgradedRelease, err := u.prepareUpgrade(name, chart, vals)
|
||||
if err != nil {
|
||||
@@ -155,7 +166,8 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
|
||||
return res, err
|
||||
}
|
||||
|
||||
if !u.DryRun {
|
||||
// Do not update for dry runs
|
||||
if !u.isDryRun() {
|
||||
u.cfg.Log("updating status for upgraded release for %s", name)
|
||||
if err := u.cfg.Releases.Update(upgradedRelease); err != nil {
|
||||
return res, err
|
||||
@@ -165,6 +177,14 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// isDryRun returns true if Upgrade is set to run as a DryRun
|
||||
func (u *Upgrade) isDryRun() bool {
|
||||
if u.DryRun || u.DryRunOption == "client" || u.DryRunOption == "server" || u.DryRunOption == "true" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// prepareUpgrade builds an upgraded release for an upgrade operation.
|
||||
func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, *release.Release, error) {
|
||||
if chart == nil {
|
||||
@@ -209,7 +229,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := chartutil.ProcessDependencies(chart, vals); err != nil {
|
||||
if err := chartutil.ProcessDependenciesWithMerge(chart, vals); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -233,11 +253,21 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun, u.EnableDNS)
|
||||
// Determine whether or not to interact with remote
|
||||
var interactWithRemote bool
|
||||
if !u.isDryRun() || u.DryRunOption == "server" || u.DryRunOption == "none" || u.DryRunOption == "false" {
|
||||
interactWithRemote = true
|
||||
}
|
||||
|
||||
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, interactWithRemote, u.EnableDNS)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if driver.ContainsSystemLabels(u.Labels) {
|
||||
return nil, nil, fmt.Errorf("user suplied labels contains system reserved label name. System labels: %+v", driver.GetSystemLabels())
|
||||
}
|
||||
|
||||
// Store an upgraded release.
|
||||
upgradedRelease := &release.Release{
|
||||
Name: name,
|
||||
@@ -253,6 +283,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
|
||||
Version: revision,
|
||||
Manifest: manifestDoc.String(),
|
||||
Hooks: hooks,
|
||||
Labels: mergeCustomLabels(lastRelease.Labels, u.Labels),
|
||||
}
|
||||
|
||||
if len(notesTxt) > 0 {
|
||||
@@ -300,7 +331,7 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR
|
||||
|
||||
toBeUpdated, err := existingResourceConflict(toBeCreated, upgradedRelease.Name, upgradedRelease.Namespace)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with update")
|
||||
return nil, errors.Wrap(err, "Unable to continue with update")
|
||||
}
|
||||
|
||||
toBeUpdated.Visit(func(r *resource.Info, err error) error {
|
||||
@@ -311,7 +342,8 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR
|
||||
return nil
|
||||
})
|
||||
|
||||
if u.DryRun {
|
||||
// Run if it is a dry run
|
||||
if u.isDryRun() {
|
||||
u.cfg.Log("dry run for %s", upgradedRelease.Name)
|
||||
if len(u.Description) > 0 {
|
||||
upgradedRelease.Info.Description = u.Description
|
||||
@@ -522,6 +554,15 @@ func (u *Upgrade) reuseValues(chart *chart.Chart, current *release.Release, newV
|
||||
return newVals, nil
|
||||
}
|
||||
|
||||
// If the ResetThenReuseValues flag is set, we use the new chart's values, but we copy the old config's values over the new config's values.
|
||||
if u.ResetThenReuseValues {
|
||||
u.cfg.Log("merging values from old release to new values")
|
||||
|
||||
newVals = chartutil.CoalesceTables(newVals, current.Config)
|
||||
|
||||
return newVals, nil
|
||||
}
|
||||
|
||||
if len(newVals) == 0 && len(current.Config) > 0 {
|
||||
u.cfg.Log("copying values from %s (v%d) to new release.", current.Name, current.Version)
|
||||
newVals = current.Config
|
||||
@@ -574,3 +615,13 @@ func objectKey(r *resource.Info) string {
|
||||
gvk := r.Object.GetObjectKind().GroupVersionKind()
|
||||
return fmt.Sprintf("%s/%s/%s/%s", gvk.GroupVersion().String(), gvk.Kind, r.Namespace, r.Name)
|
||||
}
|
||||
|
||||
func mergeCustomLabels(current, desired map[string]string) map[string]string {
|
||||
labels := mergeStrStrMaps(current, desired)
|
||||
for k, v := range labels {
|
||||
if v == "null" {
|
||||
delete(labels, k)
|
||||
}
|
||||
}
|
||||
return labels
|
||||
}
|
||||
|
||||
@@ -85,7 +85,10 @@ func ensureArchive(name string, raw *os.File) error {
|
||||
if err != nil && err != io.EOF {
|
||||
return fmt.Errorf("file '%s' cannot be read: %s", name, err)
|
||||
}
|
||||
if contentType := http.DetectContentType(buffer); contentType != "application/x-gzip" {
|
||||
|
||||
// Helm may identify achieve of the application/x-gzip as application/vnd.ms-fontobject.
|
||||
// Fix for: https://github.com/helm/helm/issues/12261
|
||||
if contentType := http.DetectContentType(buffer); contentType != "application/x-gzip" && !isGZipApplication(buffer) {
|
||||
// TODO: Is there a way to reliably test if a file content is YAML? ghodss/yaml accepts a wide
|
||||
// variety of content (Makefile, .zshrc) as valid YAML without errors.
|
||||
|
||||
@@ -98,6 +101,12 @@ func ensureArchive(name string, raw *os.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// isGZipApplication checks whether the achieve is of the application/x-gzip type.
|
||||
func isGZipApplication(data []byte) bool {
|
||||
sig := []byte("\x1F\x8B\x08")
|
||||
return bytes.HasPrefix(data, sig)
|
||||
}
|
||||
|
||||
// LoadArchiveFiles reads in files out of an archive into memory. This function
|
||||
// performs important path security checks and should always be used before
|
||||
// expanding a tarball
|
||||
|
||||
@@ -19,16 +19,15 @@ package loader
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/internal/ignore"
|
||||
"helm.sh/helm/v3/internal/sympath"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/ignore"
|
||||
)
|
||||
|
||||
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
|
||||
@@ -102,7 +101,7 @@ func LoadDir(dir string) (*chart.Chart, error) {
|
||||
return fmt.Errorf("cannot load irregular file %s as it has file mode type bits set", name)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(name)
|
||||
data, err := os.ReadFile(name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error reading %s", n)
|
||||
}
|
||||
|
||||
15
vendor/helm.sh/helm/v3/pkg/chart/metadata.go
vendored
15
vendor/helm.sh/helm/v3/pkg/chart/metadata.go
vendored
@@ -16,6 +16,7 @@ limitations under the License.
|
||||
package chart
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
@@ -110,6 +111,11 @@ func (md *Metadata) Validate() error {
|
||||
if md.Name == "" {
|
||||
return ValidationError("chart.metadata.name is required")
|
||||
}
|
||||
|
||||
if md.Name != filepath.Base(md.Name) {
|
||||
return ValidationErrorf("chart.metadata.name %q is invalid", md.Name)
|
||||
}
|
||||
|
||||
if md.Version == "" {
|
||||
return ValidationError("chart.metadata.version is required")
|
||||
}
|
||||
@@ -128,10 +134,19 @@ func (md *Metadata) Validate() error {
|
||||
|
||||
// Aliases need to be validated here to make sure that the alias name does
|
||||
// not contain any illegal characters.
|
||||
dependencies := map[string]*Dependency{}
|
||||
for _, dependency := range md.Dependencies {
|
||||
if err := dependency.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
key := dependency.Name
|
||||
if dependency.Alias != "" {
|
||||
key = dependency.Alias
|
||||
}
|
||||
if dependencies[key] != nil {
|
||||
return ValidationErrorf("more than one dependency with name or alias %q", key)
|
||||
}
|
||||
dependencies[key] = dependency
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package chartutil
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -29,7 +28,7 @@ import (
|
||||
|
||||
// LoadChartfile loads a Chart.yaml file into a *chart.Metadata.
|
||||
func LoadChartfile(filename string) (*chart.Metadata, error) {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
b, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -55,7 +54,7 @@ func SaveChartfile(filename string, cf *chart.Metadata) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(filename, out, 0644)
|
||||
return os.WriteFile(filename, out, 0644)
|
||||
}
|
||||
|
||||
// IsChartDir validate a chart directory.
|
||||
@@ -73,7 +72,7 @@ func IsChartDir(dirName string) (bool, error) {
|
||||
return false, errors.Errorf("no %s exists in directory %q", ChartfileName, dirName)
|
||||
}
|
||||
|
||||
chartYamlContent, err := ioutil.ReadFile(chartYaml)
|
||||
chartYamlContent, err := os.ReadFile(chartYaml)
|
||||
if err != nil {
|
||||
return false, errors.Errorf("cannot read %s in directory %q", ChartfileName, dirName)
|
||||
}
|
||||
|
||||
118
vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go
vendored
118
vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go
vendored
@@ -37,12 +37,42 @@ func concatPrefix(a, b string) string {
|
||||
//
|
||||
// Values are coalesced together using the following rules:
|
||||
//
|
||||
// - Values in a higher level chart always override values in a lower-level
|
||||
// dependency chart
|
||||
// - Scalar values and arrays are replaced, maps are merged
|
||||
// - A chart has access to all of the variables for it, as well as all of
|
||||
// the values destined for its dependencies.
|
||||
// - Values in a higher level chart always override values in a lower-level
|
||||
// dependency chart
|
||||
// - Scalar values and arrays are replaced, maps are merged
|
||||
// - A chart has access to all of the variables for it, as well as all of
|
||||
// the values destined for its dependencies.
|
||||
func CoalesceValues(chrt *chart.Chart, vals map[string]interface{}) (Values, error) {
|
||||
valsCopy, err := copyValues(vals)
|
||||
if err != nil {
|
||||
return vals, err
|
||||
}
|
||||
return coalesce(log.Printf, chrt, valsCopy, "", false)
|
||||
}
|
||||
|
||||
// MergeValues is used to merge the values in a chart and its subcharts. This
|
||||
// is different from Coalescing as nil/null values are preserved.
|
||||
//
|
||||
// Values are coalesced together using the following rules:
|
||||
//
|
||||
// - Values in a higher level chart always override values in a lower-level
|
||||
// dependency chart
|
||||
// - Scalar values and arrays are replaced, maps are merged
|
||||
// - A chart has access to all of the variables for it, as well as all of
|
||||
// the values destined for its dependencies.
|
||||
//
|
||||
// Retaining Nils is useful when processes early in a Helm action or business
|
||||
// logic need to retain them for when Coalescing will happen again later in the
|
||||
// business logic.
|
||||
func MergeValues(chrt *chart.Chart, vals map[string]interface{}) (Values, error) {
|
||||
valsCopy, err := copyValues(vals)
|
||||
if err != nil {
|
||||
return vals, err
|
||||
}
|
||||
return coalesce(log.Printf, chrt, valsCopy, "", true)
|
||||
}
|
||||
|
||||
func copyValues(vals map[string]interface{}) (Values, error) {
|
||||
v, err := copystructure.Copy(vals)
|
||||
if err != nil {
|
||||
return vals, err
|
||||
@@ -53,21 +83,26 @@ func CoalesceValues(chrt *chart.Chart, vals map[string]interface{}) (Values, err
|
||||
if valsCopy == nil {
|
||||
valsCopy = make(map[string]interface{})
|
||||
}
|
||||
return coalesce(log.Printf, chrt, valsCopy, "")
|
||||
|
||||
return valsCopy, nil
|
||||
}
|
||||
|
||||
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(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)
|
||||
// This is a helper function for CoalesceValues and MergeValues.
|
||||
//
|
||||
// Note, the merge argument specifies whether this is being used by MergeValues
|
||||
// or CoalesceValues. Coalescing removes null values and their keys in some
|
||||
// situations while merging keeps the null values.
|
||||
func coalesce(printf printFn, ch *chart.Chart, dest map[string]interface{}, prefix string, merge bool) (map[string]interface{}, error) {
|
||||
coalesceValues(printf, ch, dest, prefix, merge)
|
||||
return coalesceDeps(printf, ch, dest, prefix, merge)
|
||||
}
|
||||
|
||||
// coalesceDeps coalesces the dependencies of the given chart.
|
||||
func coalesceDeps(printf printFn, chrt *chart.Chart, dest map[string]interface{}, prefix string) (map[string]interface{}, error) {
|
||||
func coalesceDeps(printf printFn, chrt *chart.Chart, dest map[string]interface{}, prefix string, merge bool) (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.
|
||||
@@ -78,13 +113,11 @@ func coalesceDeps(printf printFn, chrt *chart.Chart, dest map[string]interface{}
|
||||
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(printf, dvmap, dest, subPrefix)
|
||||
|
||||
coalesceGlobals(printf, dvmap, dest, subPrefix, merge)
|
||||
// Now coalesce the rest of the values.
|
||||
var err error
|
||||
dest[subchart.Name()], err = coalesce(printf, subchart, dvmap, subPrefix)
|
||||
dest[subchart.Name()], err = coalesce(printf, subchart, dvmap, subPrefix, merge)
|
||||
if err != nil {
|
||||
return dest, err
|
||||
}
|
||||
@@ -96,7 +129,7 @@ func coalesceDeps(printf printFn, chrt *chart.Chart, dest map[string]interface{}
|
||||
// coalesceGlobals copies the globals out of src and merges them into dest.
|
||||
//
|
||||
// For convenience, returns dest.
|
||||
func coalesceGlobals(printf printFn, dest, src map[string]interface{}, prefix string) {
|
||||
func coalesceGlobals(printf printFn, dest, src map[string]interface{}, prefix string, _ bool) {
|
||||
var dg, sg map[string]interface{}
|
||||
|
||||
if destglob, ok := dest[GlobalKey]; !ok {
|
||||
@@ -130,7 +163,10 @@ func coalesceGlobals(printf printFn, dest, src map[string]interface{}, prefix st
|
||||
// Basically, we reverse order of coalesce here to merge
|
||||
// top-down.
|
||||
subPrefix := concatPrefix(prefix, key)
|
||||
coalesceTablesFullKey(printf, vv, destvmap, subPrefix)
|
||||
// In this location coalesceTablesFullKey should always have
|
||||
// merge set to true. The output of coalesceGlobals is run
|
||||
// through coalesce where any nils will be removed.
|
||||
coalesceTablesFullKey(printf, vv, destvmap, subPrefix, true)
|
||||
dg[key] = vv
|
||||
}
|
||||
}
|
||||
@@ -156,12 +192,38 @@ 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(printf printFn, c *chart.Chart, v map[string]interface{}, prefix string) {
|
||||
func coalesceValues(printf printFn, c *chart.Chart, v map[string]interface{}, prefix string, merge bool) {
|
||||
subPrefix := concatPrefix(prefix, c.Metadata.Name)
|
||||
for key, val := range c.Values {
|
||||
|
||||
// Using c.Values directly when coalescing a table can cause problems where
|
||||
// the original c.Values is altered. Creating a deep copy stops the problem.
|
||||
// This section is fault-tolerant as there is no ability to return an error.
|
||||
valuesCopy, err := copystructure.Copy(c.Values)
|
||||
var vc map[string]interface{}
|
||||
var ok bool
|
||||
if err != nil {
|
||||
// If there is an error something is wrong with copying c.Values it
|
||||
// means there is a problem in the deep copying package or something
|
||||
// wrong with c.Values. In this case we will use c.Values and report
|
||||
// an error.
|
||||
printf("warning: unable to copy values, err: %s", err)
|
||||
vc = c.Values
|
||||
} else {
|
||||
vc, ok = valuesCopy.(map[string]interface{})
|
||||
if !ok {
|
||||
// c.Values has a map[string]interface{} structure. If the copy of
|
||||
// it cannot be treated as map[string]interface{} there is something
|
||||
// strangely wrong. Log it and use c.Values
|
||||
printf("warning: unable to convert values copy to values type")
|
||||
vc = c.Values
|
||||
}
|
||||
}
|
||||
|
||||
for key, val := range vc {
|
||||
if value, ok := v[key]; ok {
|
||||
if value == nil {
|
||||
// When the YAML value is null, we remove the value's key.
|
||||
if value == nil && !merge {
|
||||
// When the YAML value is null and we are coalescing instead of
|
||||
// merging, we remove the value's key.
|
||||
// This allows Helm's various sources of values (value files or --set) to
|
||||
// remove incompatible keys from any previous chart, file, or set values.
|
||||
delete(v, key)
|
||||
@@ -177,7 +239,7 @@ func coalesceValues(printf printFn, c *chart.Chart, v map[string]interface{}, pr
|
||||
} else {
|
||||
// Because v has higher precedence than nv, dest values override src
|
||||
// values.
|
||||
coalesceTablesFullKey(printf, dest, src, concatPrefix(subPrefix, key))
|
||||
coalesceTablesFullKey(printf, dest, src, concatPrefix(subPrefix, key), merge)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -191,13 +253,17 @@ func coalesceValues(printf printFn, c *chart.Chart, v map[string]interface{}, pr
|
||||
//
|
||||
// dest is considered authoritative.
|
||||
func CoalesceTables(dst, src map[string]interface{}) map[string]interface{} {
|
||||
return coalesceTablesFullKey(log.Printf, dst, src, "")
|
||||
return coalesceTablesFullKey(log.Printf, dst, src, "", false)
|
||||
}
|
||||
|
||||
func MergeTables(dst, src map[string]interface{}) map[string]interface{} {
|
||||
return coalesceTablesFullKey(log.Printf, dst, src, "", true)
|
||||
}
|
||||
|
||||
// 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{} {
|
||||
func coalesceTablesFullKey(printf printFn, dst, src map[string]interface{}, prefix string, merge bool) map[string]interface{} {
|
||||
// When --reuse-values is set but there are no modifications yet, return new values
|
||||
if src == nil {
|
||||
return dst
|
||||
@@ -209,13 +275,13 @@ func coalesceTablesFullKey(printf printFn, dst, src map[string]interface{}, pref
|
||||
// values.
|
||||
for key, val := range src {
|
||||
fullkey := concatPrefix(prefix, key)
|
||||
if dv, ok := dst[key]; ok && dv == nil {
|
||||
if dv, ok := dst[key]; ok && !merge && dv == nil {
|
||||
delete(dst, key)
|
||||
} else if !ok {
|
||||
dst[key] = val
|
||||
} else if istable(val) {
|
||||
if istable(dv) {
|
||||
coalesceTablesFullKey(printf, dv.(map[string]interface{}), val.(map[string]interface{}), fullkey)
|
||||
coalesceTablesFullKey(printf, dv.(map[string]interface{}), val.(map[string]interface{}), fullkey, merge)
|
||||
} else {
|
||||
printf("warning: cannot overwrite table with non table for %s (%v)", fullkey, val)
|
||||
}
|
||||
|
||||
60
vendor/helm.sh/helm/v3/pkg/chartutil/create.go
vendored
60
vendor/helm.sh/helm/v3/pkg/chartutil/create.go
vendored
@@ -19,7 +19,6 @@ package chartutil
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -122,6 +121,8 @@ fullnameOverride: ""
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Automatically mount a ServiceAccount's API credentials?
|
||||
automount: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
@@ -129,6 +130,7 @@ serviceAccount:
|
||||
name: ""
|
||||
|
||||
podAnnotations: {}
|
||||
podLabels: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
@@ -173,6 +175,15 @@ resources: {}
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
@@ -180,6 +191,19 @@ autoscaling:
|
||||
targetCPUUtilizationPercentage: 80
|
||||
# targetMemoryUtilizationPercentage: 80
|
||||
|
||||
# Additional volumes on the output Deployment definition.
|
||||
volumes: []
|
||||
# - name: foo
|
||||
# secret:
|
||||
# secretName: mysecret
|
||||
# optional: false
|
||||
|
||||
# Additional volumeMounts on the output Deployment definition.
|
||||
volumeMounts: []
|
||||
# - name: foo
|
||||
# mountPath: "/etc/foo"
|
||||
# readOnly: true
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
@@ -295,7 +319,10 @@ spec:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "<CHARTNAME>.selectorLabels" . | nindent 8 }}
|
||||
{{- include "<CHARTNAME>.labels" . | nindent 8 }}
|
||||
{{- with .Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
@@ -315,15 +342,19 @@ spec:
|
||||
containerPort: {{ .Values.service.port }}
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- with .Values.volumeMounts }}
|
||||
volumeMounts:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.volumes }}
|
||||
volumes:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
@@ -366,11 +397,12 @@ metadata:
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
|
||||
{{- end }}
|
||||
`
|
||||
|
||||
const defaultHorizontalPodAutoscaler = `{{- if .Values.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2beta1
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "<CHARTNAME>.fullname" . }}
|
||||
@@ -388,13 +420,17 @@ spec:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
`
|
||||
@@ -673,7 +709,7 @@ func writeFile(name string, content []byte) error {
|
||||
if err := os.MkdirAll(filepath.Dir(name), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(name, content, 0644)
|
||||
return os.WriteFile(name, content, 0644)
|
||||
}
|
||||
|
||||
func validateChartName(name string) error {
|
||||
|
||||
@@ -19,15 +19,29 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/copystructure"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
)
|
||||
|
||||
// ProcessDependencies checks through this chart's dependencies, processing accordingly.
|
||||
//
|
||||
// TODO: For Helm v4 this can be combined with or turned into ProcessDependenciesWithMerge
|
||||
func ProcessDependencies(c *chart.Chart, v Values) error {
|
||||
if err := processDependencyEnabled(c, v, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
return processDependencyImportValues(c)
|
||||
return processDependencyImportValues(c, false)
|
||||
}
|
||||
|
||||
// ProcessDependenciesWithMerge checks through this chart's dependencies, processing accordingly.
|
||||
// It is similar to ProcessDependencies but it does not remove nil values during
|
||||
// the import/export handling process.
|
||||
func ProcessDependenciesWithMerge(c *chart.Chart, v Values) error {
|
||||
if err := processDependencyEnabled(c, v, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
return processDependencyImportValues(c, true)
|
||||
}
|
||||
|
||||
// processDependencyConditions disables charts based on condition path value in values
|
||||
@@ -45,9 +59,8 @@ func processDependencyConditions(reqs []*chart.Dependency, cvals Values, cpath s
|
||||
if bv, ok := vv.(bool); ok {
|
||||
r.Enabled = bv
|
||||
break
|
||||
} else {
|
||||
log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name)
|
||||
}
|
||||
log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name)
|
||||
} else if _, ok := err.(ErrNoValue); !ok {
|
||||
// this is a real error
|
||||
log.Printf("Warning: PathValue returned error %v", err)
|
||||
@@ -137,6 +150,9 @@ Loop:
|
||||
}
|
||||
|
||||
for _, req := range c.Metadata.Dependencies {
|
||||
if req == nil {
|
||||
continue
|
||||
}
|
||||
if chartDependency := getAliasDependency(c.Dependencies(), req); chartDependency != nil {
|
||||
chartDependencies = append(chartDependencies, chartDependency)
|
||||
}
|
||||
@@ -217,12 +233,18 @@ func set(path []string, data map[string]interface{}) map[string]interface{} {
|
||||
}
|
||||
|
||||
// processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field.
|
||||
func processImportValues(c *chart.Chart) error {
|
||||
func processImportValues(c *chart.Chart, merge bool) error {
|
||||
if c.Metadata.Dependencies == nil {
|
||||
return nil
|
||||
}
|
||||
// combine chart values and empty config to get Values
|
||||
cvals, err := CoalesceValues(c, nil)
|
||||
var cvals Values
|
||||
var err error
|
||||
if merge {
|
||||
cvals, err = MergeValues(c, nil)
|
||||
} else {
|
||||
cvals, err = CoalesceValues(c, nil)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -248,7 +270,11 @@ func processImportValues(c *chart.Chart) error {
|
||||
continue
|
||||
}
|
||||
// create value map from child to be merged into parent
|
||||
b = CoalesceTables(cvals, pathToMap(parent, vv.AsMap()))
|
||||
if merge {
|
||||
b = MergeTables(b, pathToMap(parent, vv.AsMap()))
|
||||
} else {
|
||||
b = CoalesceTables(b, pathToMap(parent, vv.AsMap()))
|
||||
}
|
||||
case string:
|
||||
child := "exports." + iv
|
||||
outiv = append(outiv, map[string]string{
|
||||
@@ -260,26 +286,71 @@ func processImportValues(c *chart.Chart) error {
|
||||
log.Printf("Warning: ImportValues missing table: %v", err)
|
||||
continue
|
||||
}
|
||||
b = CoalesceTables(b, vm.AsMap())
|
||||
if merge {
|
||||
b = MergeTables(b, vm.AsMap())
|
||||
} else {
|
||||
b = CoalesceTables(b, vm.AsMap())
|
||||
}
|
||||
}
|
||||
}
|
||||
// set our formatted import values
|
||||
r.ImportValues = outiv
|
||||
}
|
||||
|
||||
// set the new values
|
||||
c.Values = CoalesceTables(cvals, b)
|
||||
// Imported values from a child to a parent chart have a lower priority than
|
||||
// the parents values. This enables parent charts to import a large section
|
||||
// from a child and then override select parts. This is why b is merged into
|
||||
// cvals in the code below and not the other way around.
|
||||
if merge {
|
||||
// deep copying the cvals as there are cases where pointers can end
|
||||
// up in the cvals when they are copied onto b in ways that break things.
|
||||
cvals = deepCopyMap(cvals)
|
||||
c.Values = MergeTables(cvals, b)
|
||||
} else {
|
||||
// Trimming the nil values from cvals is needed for backwards compatibility.
|
||||
// Previously, the b value had been populated with cvals along with some
|
||||
// overrides. This caused the coalescing functionality to remove the
|
||||
// nil/null values. This trimming is for backwards compat.
|
||||
cvals = trimNilValues(cvals)
|
||||
c.Values = CoalesceTables(cvals, b)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopyMap(vals map[string]interface{}) map[string]interface{} {
|
||||
valsCopy, err := copystructure.Copy(vals)
|
||||
if err != nil {
|
||||
return vals
|
||||
}
|
||||
return valsCopy.(map[string]interface{})
|
||||
}
|
||||
|
||||
func trimNilValues(vals map[string]interface{}) map[string]interface{} {
|
||||
valsCopy, err := copystructure.Copy(vals)
|
||||
if err != nil {
|
||||
return vals
|
||||
}
|
||||
valsCopyMap := valsCopy.(map[string]interface{})
|
||||
for key, val := range valsCopyMap {
|
||||
if val == nil {
|
||||
// Iterate over the values and remove nil keys
|
||||
delete(valsCopyMap, key)
|
||||
} else if istable(val) {
|
||||
// Recursively call into ourselves to remove keys from inner tables
|
||||
valsCopyMap[key] = trimNilValues(val.(map[string]interface{}))
|
||||
}
|
||||
}
|
||||
|
||||
return valsCopyMap
|
||||
}
|
||||
|
||||
// processDependencyImportValues imports specified chart values from child to parent.
|
||||
func processDependencyImportValues(c *chart.Chart) error {
|
||||
func processDependencyImportValues(c *chart.Chart, merge bool) error {
|
||||
for _, d := range c.Dependencies() {
|
||||
// recurse
|
||||
if err := processDependencyImportValues(d); err != nil {
|
||||
if err := processDependencyImportValues(d, merge); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return processImportValues(c)
|
||||
return processImportValues(c, merge)
|
||||
}
|
||||
|
||||
9
vendor/helm.sh/helm/v3/pkg/chartutil/doc.go
vendored
9
vendor/helm.sh/helm/v3/pkg/chartutil/doc.go
vendored
@@ -14,16 +14,17 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*Package chartutil contains tools for working with charts.
|
||||
/*
|
||||
Package chartutil contains tools for working with charts.
|
||||
|
||||
Charts are described in the chart package (pkg/chart).
|
||||
This package provides utilities for serializing and deserializing charts.
|
||||
|
||||
A chart can be represented on the file system in one of two ways:
|
||||
|
||||
- As a directory that contains a Chart.yaml file and other chart things.
|
||||
- As a tarred gzipped file containing a directory that then contains a
|
||||
Chart.yaml file.
|
||||
- As a directory that contains a Chart.yaml file and other chart things.
|
||||
- As a tarred gzipped file containing a directory that then contains a
|
||||
Chart.yaml file.
|
||||
|
||||
This package provides utilities for working with those file formats.
|
||||
|
||||
|
||||
@@ -33,3 +33,11 @@ type ErrNoValue struct {
|
||||
}
|
||||
|
||||
func (e ErrNoValue) Error() string { return fmt.Sprintf("%q is not a value", e.Key) }
|
||||
|
||||
type ErrInvalidChartName struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (e ErrInvalidChartName) Error() string {
|
||||
return fmt.Sprintf("%q is not a valid chart name", e.Name)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package chartutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -72,7 +71,7 @@ func Expand(dir string, r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(outpath, file.Data, 0644); err != nil {
|
||||
if err := os.WriteFile(outpath, file.Data, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
20
vendor/helm.sh/helm/v3/pkg/chartutil/save.go
vendored
20
vendor/helm.sh/helm/v3/pkg/chartutil/save.go
vendored
@@ -39,6 +39,10 @@ var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")
|
||||
// directory, writing the chart's contents to that subdirectory.
|
||||
func SaveDir(c *chart.Chart, dest string) error {
|
||||
// Create the chart directory
|
||||
err := validateName(c.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outdir := filepath.Join(dest, c.Name())
|
||||
if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() {
|
||||
return errors.Errorf("file %s already exists and is not a directory", outdir)
|
||||
@@ -149,6 +153,10 @@ func Save(c *chart.Chart, outDir string) (string, error) {
|
||||
}
|
||||
|
||||
func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
|
||||
err := validateName(c.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
base := filepath.Join(prefix, c.Name())
|
||||
|
||||
// Pull out the dependencies of a v1 Chart, since there's no way
|
||||
@@ -242,3 +250,15 @@ func writeToTar(out *tar.Writer, name string, body []byte) error {
|
||||
_, err := out.Write(body)
|
||||
return err
|
||||
}
|
||||
|
||||
// If the name has directory name has characters which would change the location
|
||||
// they need to be removed.
|
||||
func validateName(name string) error {
|
||||
nname := filepath.Base(name)
|
||||
|
||||
if nname != name {
|
||||
return ErrInvalidChartName{name}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ package chartutil
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -114,7 +114,7 @@ func ReadValues(data []byte) (vals Values, err error) {
|
||||
|
||||
// ReadValuesFile will parse a YAML file into a map of values.
|
||||
func ReadValuesFile(filename string) (Values, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return map[string]interface{}{}, err
|
||||
}
|
||||
|
||||
21
vendor/helm.sh/helm/v3/pkg/cli/environment.go
vendored
21
vendor/helm.sh/helm/v3/pkg/cli/environment.go
vendored
@@ -44,6 +44,9 @@ const defaultMaxHistory = 10
|
||||
// defaultBurstLimit sets the default client-side throttling limit
|
||||
const defaultBurstLimit = 100
|
||||
|
||||
// defaultQPS sets the default QPS value to 0 to to use library defaults unless specified
|
||||
const defaultQPS = float32(0)
|
||||
|
||||
// EnvSettings describes all of the environment settings.
|
||||
type EnvSettings struct {
|
||||
namespace string
|
||||
@@ -83,6 +86,8 @@ type EnvSettings struct {
|
||||
MaxHistory int
|
||||
// BurstLimit is the default client-side throttling limit.
|
||||
BurstLimit int
|
||||
// QPS is queries per second which may be used to avoid throttling.
|
||||
QPS float32
|
||||
}
|
||||
|
||||
func New() *EnvSettings {
|
||||
@@ -102,6 +107,7 @@ func New() *EnvSettings {
|
||||
RepositoryConfig: envOr("HELM_REPOSITORY_CONFIG", helmpath.ConfigPath("repositories.yaml")),
|
||||
RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")),
|
||||
BurstLimit: envIntOr("HELM_BURST_LIMIT", defaultBurstLimit),
|
||||
QPS: envFloat32Or("HELM_QPS", defaultQPS),
|
||||
}
|
||||
env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG"))
|
||||
|
||||
@@ -119,6 +125,7 @@ func New() *EnvSettings {
|
||||
ImpersonateGroup: &env.KubeAsGroups,
|
||||
WrapConfigFn: func(config *rest.Config) *rest.Config {
|
||||
config.Burst = env.BurstLimit
|
||||
config.QPS = env.QPS
|
||||
config.Wrap(func(rt http.RoundTripper) http.RoundTripper {
|
||||
return &retryingRoundTripper{wrapped: rt}
|
||||
})
|
||||
@@ -146,6 +153,7 @@ func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the file containing repository names and URLs")
|
||||
fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the file containing cached repository indexes")
|
||||
fs.IntVar(&s.BurstLimit, "burst-limit", s.BurstLimit, "client-side default throttling limit")
|
||||
fs.Float32Var(&s.QPS, "qps", s.QPS, "queries per second used when communicating with the Kubernetes API, not including bursting")
|
||||
}
|
||||
|
||||
func envOr(name, def string) string {
|
||||
@@ -179,6 +187,18 @@ func envIntOr(name string, def int) int {
|
||||
return ret
|
||||
}
|
||||
|
||||
func envFloat32Or(name string, def float32) float32 {
|
||||
if name == "" {
|
||||
return def
|
||||
}
|
||||
envVal := envOr(name, strconv.FormatFloat(float64(def), 'f', 2, 32))
|
||||
ret, err := strconv.ParseFloat(envVal, 32)
|
||||
if err != nil {
|
||||
return def
|
||||
}
|
||||
return float32(ret)
|
||||
}
|
||||
|
||||
func envCSV(name string) (ls []string) {
|
||||
trimmed := strings.Trim(os.Getenv(name), ", ")
|
||||
if trimmed != "" {
|
||||
@@ -201,6 +221,7 @@ func (s *EnvSettings) EnvVars() map[string]string {
|
||||
"HELM_NAMESPACE": s.Namespace(),
|
||||
"HELM_MAX_HISTORY": strconv.Itoa(s.MaxHistory),
|
||||
"HELM_BURST_LIMIT": strconv.Itoa(s.BurstLimit),
|
||||
"HELM_QPS": strconv.FormatFloat(float64(s.QPS), 'f', 2, 32),
|
||||
|
||||
// broken, these are populated from helm flags and not kubeconfig.
|
||||
"HELM_KUBECONTEXT": s.KubeContext,
|
||||
|
||||
@@ -184,11 +184,11 @@ func (c *ChartDownloader) getOciURI(ref, version string, u *url.URL) (*url.URL,
|
||||
//
|
||||
// A version is a SemVer string (1.2.3-beta.1+f334a6789).
|
||||
//
|
||||
// - For fully qualified URLs, the version will be ignored (since URLs aren't versioned)
|
||||
// - For a chart reference
|
||||
// * If version is non-empty, this will return the URL for that version
|
||||
// * If version is empty, this will return the URL for the latest version
|
||||
// * If no version can be found, an error is returned
|
||||
// - For fully qualified URLs, the version will be ignored (since URLs aren't versioned)
|
||||
// - For a chart reference
|
||||
// - If version is non-empty, this will return the URL for that version
|
||||
// - If version is empty, this will return the URL for the latest version
|
||||
// - If no version can be found, an error is returned
|
||||
func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, error) {
|
||||
u, err := url.Parse(ref)
|
||||
if err != nil {
|
||||
|
||||
3
vendor/helm.sh/helm/v3/pkg/downloader/doc.go
vendored
3
vendor/helm.sh/helm/v3/pkg/downloader/doc.go
vendored
@@ -13,7 +13,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*Package downloader provides a library for downloading charts.
|
||||
/*
|
||||
Package downloader provides a library for downloading charts.
|
||||
|
||||
This package contains various tools for downloading charts from repository
|
||||
servers, and then storing them in Helm-specific directory structures. This
|
||||
|
||||
13
vendor/helm.sh/helm/v3/pkg/downloader/manager.go
vendored
13
vendor/helm.sh/helm/v3/pkg/downloader/manager.go
vendored
@@ -20,7 +20,6 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -249,7 +248,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
|
||||
destPath := filepath.Join(m.ChartPath, "charts")
|
||||
tmpPath := filepath.Join(m.ChartPath, "tmpcharts")
|
||||
|
||||
// Check if 'charts' directory is not actally a directory. If it does not exist, create it.
|
||||
// Check if 'charts' directory is not actually 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)
|
||||
@@ -668,6 +667,7 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.CachePath = m.RepositoryCache
|
||||
wg.Add(1)
|
||||
go func(r *repo.ChartRepository) {
|
||||
if _, err := r.DownloadIndexFile(); err != nil {
|
||||
@@ -714,15 +714,21 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*
|
||||
var entry repo.ChartVersions
|
||||
entry, err = findEntryByName(name, cr)
|
||||
if err != nil {
|
||||
// TODO: Where linting is skipped in this function we should
|
||||
// refactor to remove naked returns while ensuring the same
|
||||
// behavior
|
||||
//nolint:nakedret
|
||||
return
|
||||
}
|
||||
var ve *repo.ChartVersion
|
||||
ve, err = findVersionedEntry(version, entry)
|
||||
if err != nil {
|
||||
//nolint:nakedret
|
||||
return
|
||||
}
|
||||
url, err = normalizeURL(repoURL, ve.URLs[0])
|
||||
if err != nil {
|
||||
//nolint:nakedret
|
||||
return
|
||||
}
|
||||
username = cr.Config.Username
|
||||
@@ -732,6 +738,7 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*
|
||||
caFile = cr.Config.CAFile
|
||||
certFile = cr.Config.CertFile
|
||||
keyFile = cr.Config.KeyFile
|
||||
//nolint:nakedret
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -845,7 +852,7 @@ func writeLock(chartpath string, lock *chart.Lock, legacyLockfile bool) error {
|
||||
lockfileName = "requirements.lock"
|
||||
}
|
||||
dest := filepath.Join(chartpath, lockfileName)
|
||||
return ioutil.WriteFile(dest, data, 0644)
|
||||
return os.WriteFile(dest, data, 0644)
|
||||
}
|
||||
|
||||
// archive a dep chart from local directory and save it into destPath
|
||||
|
||||
3
vendor/helm.sh/helm/v3/pkg/engine/doc.go
vendored
3
vendor/helm.sh/helm/v3/pkg/engine/doc.go
vendored
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*Package engine implements the Go text template engine as needed for Helm.
|
||||
/*
|
||||
Package engine implements the Go text template engine as needed for Helm.
|
||||
|
||||
When Helm renders templates it does so with additional functions and different
|
||||
modes (e.g., strict, lint mode). This package handles the helm specific
|
||||
|
||||
123
vendor/helm.sh/helm/v3/pkg/engine/engine.go
vendored
123
vendor/helm.sh/helm/v3/pkg/engine/engine.go
vendored
@@ -40,16 +40,17 @@ type Engine struct {
|
||||
Strict bool
|
||||
// In LintMode, some 'required' template values may be missing, so don't fail
|
||||
LintMode bool
|
||||
// the rest config to connect to the kubernetes api
|
||||
config *rest.Config
|
||||
// optional provider of clients to talk to the Kubernetes API
|
||||
clientProvider *ClientProvider
|
||||
// EnableDNS tells the engine to allow DNS lookups when rendering templates
|
||||
EnableDNS bool
|
||||
}
|
||||
|
||||
// New creates a new instance of Engine using the passed in rest config.
|
||||
func New(config *rest.Config) Engine {
|
||||
var clientProvider ClientProvider = clientProviderFromConfig{config}
|
||||
return Engine{
|
||||
config: config,
|
||||
clientProvider: &clientProvider,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,10 +86,21 @@ func Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, erro
|
||||
|
||||
// RenderWithClient takes a chart, optional values, and value overrides, and attempts to
|
||||
// render the Go templates using the default options. This engine is client aware and so can have template
|
||||
// functions that interact with the client
|
||||
// functions that interact with the client.
|
||||
func RenderWithClient(chrt *chart.Chart, values chartutil.Values, config *rest.Config) (map[string]string, error) {
|
||||
var clientProvider ClientProvider = clientProviderFromConfig{config}
|
||||
return Engine{
|
||||
config: config,
|
||||
clientProvider: &clientProvider,
|
||||
}.Render(chrt, values)
|
||||
}
|
||||
|
||||
// RenderWithClientProvider takes a chart, optional values, and value overrides, and attempts to
|
||||
// render the Go templates using the default options. This engine is client aware and so can have template
|
||||
// functions that interact with the client.
|
||||
// This function differs from RenderWithClient in that it lets you customize the way a dynamic client is constructed.
|
||||
func RenderWithClientProvider(chrt *chart.Chart, values chartutil.Values, clientProvider ClientProvider) (map[string]string, error) {
|
||||
return Engine{
|
||||
clientProvider: &clientProvider,
|
||||
}.Render(chrt, values)
|
||||
}
|
||||
|
||||
@@ -112,13 +124,10 @@ func warnWrap(warn string) string {
|
||||
return warnStartDelim + warn + warnEndDelim
|
||||
}
|
||||
|
||||
// initFunMap creates the Engine's FuncMap and adds context-specific functions.
|
||||
func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) {
|
||||
funcMap := funcMap()
|
||||
includedNames := make(map[string]int)
|
||||
|
||||
// Add the 'include' function here so we can close over t.
|
||||
funcMap["include"] = func(name string, data interface{}) (string, error) {
|
||||
// 'include' needs to be defined in the scope of a 'tpl' template as
|
||||
// well as regular file-loaded templates.
|
||||
func includeFun(t *template.Template, includedNames map[string]int) func(string, interface{}) (string, error) {
|
||||
return func(name string, data interface{}) (string, error) {
|
||||
var buf strings.Builder
|
||||
if v, ok := includedNames[name]; ok {
|
||||
if v > recursionMaxNums {
|
||||
@@ -132,33 +141,62 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render
|
||||
includedNames[name]--
|
||||
return buf.String(), err
|
||||
}
|
||||
}
|
||||
|
||||
// Add the 'tpl' function here
|
||||
funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) {
|
||||
basePath, err := vals.PathValue("Template.BasePath")
|
||||
// As does 'tpl', so that nested calls to 'tpl' see the templates
|
||||
// defined by their enclosing contexts.
|
||||
func tplFun(parent *template.Template, includedNames map[string]int, strict bool) func(string, interface{}) (string, error) {
|
||||
return func(tpl string, vals interface{}) (string, error) {
|
||||
t, err := parent.Clone()
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "cannot retrieve Template.Basepath from values inside tpl function: %s", tpl)
|
||||
return "", errors.Wrapf(err, "cannot clone template")
|
||||
}
|
||||
|
||||
templateName, err := vals.PathValue("Template.Name")
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl)
|
||||
// Re-inject the missingkey option, see text/template issue https://github.com/golang/go/issues/43022
|
||||
// We have to go by strict from our engine configuration, as the option fields are private in Template.
|
||||
// TODO: Remove workaround (and the strict parameter) once we build only with golang versions with a fix.
|
||||
if strict {
|
||||
t.Option("missingkey=error")
|
||||
} else {
|
||||
t.Option("missingkey=zero")
|
||||
}
|
||||
|
||||
templates := map[string]renderable{
|
||||
templateName.(string): {
|
||||
tpl: tpl,
|
||||
vals: vals,
|
||||
basePath: basePath.(string),
|
||||
},
|
||||
// Re-inject 'include' so that it can close over our clone of t;
|
||||
// this lets any 'define's inside tpl be 'include'd.
|
||||
t.Funcs(template.FuncMap{
|
||||
"include": includeFun(t, includedNames),
|
||||
"tpl": tplFun(t, includedNames, strict),
|
||||
})
|
||||
|
||||
// We need a .New template, as template text which is just blanks
|
||||
// or comments after parsing out defines just addes new named
|
||||
// template definitions without changing the main template.
|
||||
// https://pkg.go.dev/text/template#Template.Parse
|
||||
// Use the parent's name for lack of a better way to identify the tpl
|
||||
// text string. (Maybe we could use a hash appended to the name?)
|
||||
t, err = t.New(parent.Name()).Parse(tpl)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "cannot parse template %q", tpl)
|
||||
}
|
||||
|
||||
result, err := e.renderWithReferences(templates, referenceTpls)
|
||||
if err != nil {
|
||||
var buf strings.Builder
|
||||
if err := t.Execute(&buf, vals); err != nil {
|
||||
return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl)
|
||||
}
|
||||
return result[templateName.(string)], nil
|
||||
|
||||
// See comment in renderWithReferences explaining the <no value> hack.
|
||||
return strings.ReplaceAll(buf.String(), "<no value>", ""), nil
|
||||
}
|
||||
}
|
||||
|
||||
// initFunMap creates the Engine's FuncMap and adds context-specific functions.
|
||||
func (e Engine) initFunMap(t *template.Template) {
|
||||
funcMap := funcMap()
|
||||
includedNames := make(map[string]int)
|
||||
|
||||
// Add the template-rendering functions here so we can close over t.
|
||||
funcMap["include"] = includeFun(t, includedNames)
|
||||
funcMap["tpl"] = tplFun(t, includedNames, e.Strict)
|
||||
|
||||
// Add the `required` function here so we can use lintMode
|
||||
funcMap["required"] = func(warn string, val interface{}) (interface{}, error) {
|
||||
@@ -194,8 +232,8 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render
|
||||
|
||||
// If we are not linting and have a cluster connection, provide a Kubernetes-backed
|
||||
// implementation.
|
||||
if !e.LintMode && e.config != nil {
|
||||
funcMap["lookup"] = NewLookupFunction(e.config)
|
||||
if !e.LintMode && e.clientProvider != nil {
|
||||
funcMap["lookup"] = newLookupFunction(*e.clientProvider)
|
||||
}
|
||||
|
||||
// When DNS lookups are not enabled override the sprig function and return
|
||||
@@ -210,13 +248,7 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render
|
||||
}
|
||||
|
||||
// render takes a map of templates/values and renders them.
|
||||
func (e Engine) render(tpls map[string]renderable) (map[string]string, error) {
|
||||
return e.renderWithReferences(tpls, tpls)
|
||||
}
|
||||
|
||||
// renderWithReferences takes a map of templates/values to render, and a map of
|
||||
// templates which can be referenced within them.
|
||||
func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable) (rendered map[string]string, err error) {
|
||||
func (e Engine) render(tpls map[string]renderable) (rendered map[string]string, err error) {
|
||||
// Basically, what we do here is start with an empty parent template and then
|
||||
// build up a list of templates -- one for each file. Once all of the templates
|
||||
// have been parsed, we loop through again and execute every template.
|
||||
@@ -238,12 +270,11 @@ func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable)
|
||||
t.Option("missingkey=zero")
|
||||
}
|
||||
|
||||
e.initFunMap(t, referenceTpls)
|
||||
e.initFunMap(t)
|
||||
|
||||
// We want to parse the templates in a predictable order. The order favors
|
||||
// higher-level (in file system) templates over deeply nested templates.
|
||||
keys := sortTemplates(tpls)
|
||||
referenceKeys := sortTemplates(referenceTpls)
|
||||
|
||||
for _, filename := range keys {
|
||||
r := tpls[filename]
|
||||
@@ -252,17 +283,6 @@ func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable)
|
||||
}
|
||||
}
|
||||
|
||||
// Adding the reference templates to the template context
|
||||
// so they can be referenced in the tpl function
|
||||
for _, filename := range referenceKeys {
|
||||
if t.Lookup(filename) == nil {
|
||||
r := referenceTpls[filename]
|
||||
if _, err := t.New(filename).Parse(r.tpl); err != nil {
|
||||
return map[string]string{}, cleanupParseError(filename, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rendered = make(map[string]string, len(keys))
|
||||
for _, filename := range keys {
|
||||
// Don't render partials. We don't care out the direct output of partials.
|
||||
@@ -391,6 +411,9 @@ func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil.
|
||||
|
||||
newParentID := c.ChartFullPath()
|
||||
for _, t := range c.Templates {
|
||||
if t == nil {
|
||||
continue
|
||||
}
|
||||
if !isTemplateValid(c, t.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
15
vendor/helm.sh/helm/v3/pkg/engine/files.go
vendored
15
vendor/helm.sh/helm/v3/pkg/engine/files.go
vendored
@@ -99,7 +99,8 @@ func (f files) Glob(pattern string) files {
|
||||
// The output will not be indented, so you will want to pipe this to the
|
||||
// 'indent' template function.
|
||||
//
|
||||
// data:
|
||||
// data:
|
||||
//
|
||||
// {{ .Files.Glob("config/**").AsConfig() | indent 4 }}
|
||||
func (f files) AsConfig() string {
|
||||
if f == nil {
|
||||
@@ -128,8 +129,9 @@ func (f files) AsConfig() string {
|
||||
// The output will not be indented, so you will want to pipe this to the
|
||||
// 'indent' template function.
|
||||
//
|
||||
// data:
|
||||
// {{ .Files.Glob("secrets/*").AsSecrets() }}
|
||||
// data:
|
||||
//
|
||||
// {{ .Files.Glob("secrets/*").AsSecrets() | indent 4 }}
|
||||
func (f files) AsSecrets() string {
|
||||
if f == nil {
|
||||
return ""
|
||||
@@ -155,6 +157,9 @@ func (f files) Lines(path string) []string {
|
||||
if f == nil || f[path] == nil {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
return strings.Split(string(f[path]), "\n")
|
||||
s := string(f[path])
|
||||
if s[len(s)-1] == '\n' {
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
return strings.Split(s, "\n")
|
||||
}
|
||||
|
||||
5
vendor/helm.sh/helm/v3/pkg/engine/funcs.go
vendored
5
vendor/helm.sh/helm/v3/pkg/engine/funcs.go
vendored
@@ -35,12 +35,11 @@ import (
|
||||
//
|
||||
// Known late-bound functions:
|
||||
//
|
||||
// - "include"
|
||||
// - "tpl"
|
||||
// - "include"
|
||||
// - "tpl"
|
||||
//
|
||||
// These are late-bound in Engine.Render(). The
|
||||
// version included in the FuncMap is a placeholder.
|
||||
//
|
||||
func funcMap() template.FuncMap {
|
||||
f := sprig.TxtFuncMap()
|
||||
delete(f, "env")
|
||||
|
||||
23
vendor/helm.sh/helm/v3/pkg/engine/lookup_func.go
vendored
23
vendor/helm.sh/helm/v3/pkg/engine/lookup_func.go
vendored
@@ -39,9 +39,28 @@ type lookupFunc = func(apiversion string, resource string, namespace string, nam
|
||||
// This function is considered deprecated, and will be renamed in Helm 4. It will no
|
||||
// longer be a public function.
|
||||
func NewLookupFunction(config *rest.Config) lookupFunc {
|
||||
return func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error) {
|
||||
return newLookupFunction(clientProviderFromConfig{config: config})
|
||||
}
|
||||
|
||||
type ClientProvider interface {
|
||||
// GetClientFor returns a dynamic.NamespaceableResourceInterface suitable for interacting with resources
|
||||
// corresponding to the provided apiVersion and kind, as well as a boolean indicating whether the resources
|
||||
// are namespaced.
|
||||
GetClientFor(apiVersion, kind string) (dynamic.NamespaceableResourceInterface, bool, error)
|
||||
}
|
||||
|
||||
type clientProviderFromConfig struct {
|
||||
config *rest.Config
|
||||
}
|
||||
|
||||
func (c clientProviderFromConfig) GetClientFor(apiVersion, kind string) (dynamic.NamespaceableResourceInterface, bool, error) {
|
||||
return getDynamicClientOnKind(apiVersion, kind, c.config)
|
||||
}
|
||||
|
||||
func newLookupFunction(clientProvider ClientProvider) lookupFunc {
|
||||
return func(apiversion string, kind string, namespace string, name string) (map[string]interface{}, error) {
|
||||
var client dynamic.ResourceInterface
|
||||
c, namespaced, err := getDynamicClientOnKind(apiversion, resource, config)
|
||||
c, namespaced, err := clientProvider.GetClientFor(apiversion, kind)
|
||||
if err != nil {
|
||||
return map[string]interface{}{}, err
|
||||
}
|
||||
|
||||
3
vendor/helm.sh/helm/v3/pkg/getter/doc.go
vendored
3
vendor/helm.sh/helm/v3/pkg/getter/doc.go
vendored
@@ -13,7 +13,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*Package getter provides a generalize tool for fetching data by scheme.
|
||||
/*
|
||||
Package getter provides a generalize tool for fetching data by scheme.
|
||||
|
||||
This provides a method by which the plugin system can load arbitrary protocol
|
||||
handlers based upon a URL scheme.
|
||||
|
||||
21
vendor/helm.sh/helm/v3/pkg/getter/getter.go
vendored
21
vendor/helm.sh/helm/v3/pkg/getter/getter.go
vendored
@@ -37,6 +37,7 @@ type options struct {
|
||||
caFile string
|
||||
unTar bool
|
||||
insecureSkipVerifyTLS bool
|
||||
plainHTTP bool
|
||||
username string
|
||||
password string
|
||||
passCredentialsAll bool
|
||||
@@ -96,6 +97,12 @@ func WithTLSClientConfig(certFile, keyFile, caFile string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithPlainHTTP(plainHTTP bool) Option {
|
||||
return func(opts *options) {
|
||||
opts.plainHTTP = plainHTTP
|
||||
}
|
||||
}
|
||||
|
||||
// WithTimeout sets the timeout for requests
|
||||
func WithTimeout(timeout time.Duration) Option {
|
||||
return func(opts *options) {
|
||||
@@ -172,9 +179,21 @@ func (p Providers) ByScheme(scheme string) (Getter, error) {
|
||||
return nil, errors.Errorf("scheme %q not supported", scheme)
|
||||
}
|
||||
|
||||
const (
|
||||
// The cost timeout references curl's default connection timeout.
|
||||
// https://github.com/curl/curl/blob/master/lib/connect.h#L40C21-L40C21
|
||||
// The helm commands are usually executed manually. Considering the acceptable waiting time, we reduced the entire request time to 120s.
|
||||
DefaultHTTPTimeout = 120
|
||||
)
|
||||
|
||||
var defaultOptions = []Option{WithTimeout(time.Second * DefaultHTTPTimeout)}
|
||||
|
||||
var httpProvider = Provider{
|
||||
Schemes: []string{"http", "https"},
|
||||
New: NewHTTPGetter,
|
||||
New: func(options ...Option) (Getter, error) {
|
||||
options = append(options, defaultOptions...)
|
||||
return NewHTTPGetter(options...)
|
||||
},
|
||||
}
|
||||
|
||||
var ociProvider = Provider{
|
||||
|
||||
@@ -123,8 +123,8 @@ func (g *HTTPGetter) httpClient() (*http.Client, error) {
|
||||
}
|
||||
})
|
||||
|
||||
if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" {
|
||||
tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile)
|
||||
if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" || g.opts.insecureSkipVerifyTLS {
|
||||
tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile, g.opts.insecureSkipVerifyTLS)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't create TLS config for client")
|
||||
}
|
||||
|
||||
97
vendor/helm.sh/helm/v3/pkg/getter/ocigetter.go
vendored
97
vendor/helm.sh/helm/v3/pkg/getter/ocigetter.go
vendored
@@ -18,14 +18,22 @@ package getter
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"helm.sh/helm/v3/internal/tlsutil"
|
||||
"helm.sh/helm/v3/internal/urlutil"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
)
|
||||
|
||||
// OCIGetter is the default HTTP(/S) backend handler
|
||||
type OCIGetter struct {
|
||||
opts options
|
||||
opts options
|
||||
transport *http.Transport
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// Get performs a Get from repo.Getter and returns the body.
|
||||
@@ -38,6 +46,15 @@ func (g *OCIGetter) Get(href string, options ...Option) (*bytes.Buffer, error) {
|
||||
|
||||
func (g *OCIGetter) get(href string) (*bytes.Buffer, error) {
|
||||
client := g.opts.registryClient
|
||||
// if the user has already provided a configured registry client, use it,
|
||||
// this is particularly true when user has his own way of handling the client credentials.
|
||||
if client == nil {
|
||||
c, err := g.newRegistryClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client = c
|
||||
}
|
||||
|
||||
ref := strings.TrimPrefix(href, fmt.Sprintf("%s://", registry.OCIScheme))
|
||||
|
||||
@@ -63,18 +80,7 @@ func (g *OCIGetter) get(href string) (*bytes.Buffer, error) {
|
||||
|
||||
// NewOCIGetter constructs a valid http/https client as a Getter
|
||||
func NewOCIGetter(ops ...Option) (Getter, error) {
|
||||
registryClient, err := registry.NewClient(
|
||||
registry.ClientOptEnableCache(true),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := OCIGetter{
|
||||
opts: options{
|
||||
registryClient: registryClient,
|
||||
},
|
||||
}
|
||||
var client OCIGetter
|
||||
|
||||
for _, opt := range ops {
|
||||
opt(&client.opts)
|
||||
@@ -82,3 +88,68 @@ func NewOCIGetter(ops ...Option) (Getter, error) {
|
||||
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
func (g *OCIGetter) newRegistryClient() (*registry.Client, error) {
|
||||
if g.opts.transport != nil {
|
||||
client, err := registry.NewClient(
|
||||
registry.ClientOptHTTPClient(&http.Client{
|
||||
Transport: g.opts.transport,
|
||||
Timeout: g.opts.timeout,
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
g.once.Do(func() {
|
||||
g.transport = &http.Transport{
|
||||
// From https://github.com/google/go-containerregistry/blob/31786c6cbb82d6ec4fb8eb79cd9387905130534e/pkg/v1/remote/options.go#L87
|
||||
DisableCompression: true,
|
||||
DialContext: (&net.Dialer{
|
||||
// By default we wrap the transport in retries, so reduce the
|
||||
// default dial timeout to 5s to avoid 5x 30s of connection
|
||||
// timeouts when doing the "ping" on certain http registries.
|
||||
Timeout: 5 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
})
|
||||
|
||||
if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" || g.opts.insecureSkipVerifyTLS {
|
||||
tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile, g.opts.insecureSkipVerifyTLS)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create TLS config for client: %w", err)
|
||||
}
|
||||
|
||||
sni, err := urlutil.ExtractHostname(g.opts.url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConf.ServerName = sni
|
||||
|
||||
g.transport.TLSClientConfig = tlsConf
|
||||
}
|
||||
|
||||
opts := []registry.ClientOption{registry.ClientOptHTTPClient(&http.Client{
|
||||
Transport: g.transport,
|
||||
Timeout: g.opts.timeout,
|
||||
})}
|
||||
if g.opts.plainHTTP {
|
||||
opts = append(opts, registry.ClientOptPlainHTTP())
|
||||
}
|
||||
|
||||
client, err := registry.NewClient(opts...)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package getter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -62,6 +63,13 @@ type pluginGetter struct {
|
||||
opts options
|
||||
}
|
||||
|
||||
func (p *pluginGetter) setupOptionsEnv(env []string) []string {
|
||||
env = append(env, fmt.Sprintf("HELM_PLUGIN_USERNAME=%s", p.opts.username))
|
||||
env = append(env, fmt.Sprintf("HELM_PLUGIN_PASSWORD=%s", p.opts.password))
|
||||
env = append(env, fmt.Sprintf("HELM_PLUGIN_PASS_CREDENTIALS_ALL=%t", p.opts.passCredentialsAll))
|
||||
return env
|
||||
}
|
||||
|
||||
// Get runs downloader plugin command
|
||||
func (p *pluginGetter) Get(href string, options ...Option) (*bytes.Buffer, error) {
|
||||
for _, opt := range options {
|
||||
@@ -71,7 +79,7 @@ func (p *pluginGetter) Get(href string, options ...Option) (*bytes.Buffer, error
|
||||
argv := append(commands[1:], p.opts.certFile, p.opts.keyFile, p.opts.caFile, href)
|
||||
prog := exec.Command(filepath.Join(p.base, commands[0]), argv...)
|
||||
plugin.SetupPluginEnv(p.settings, p.name, p.base)
|
||||
prog.Env = os.Environ()
|
||||
prog.Env = p.setupOptionsEnv(os.Environ())
|
||||
buf := bytes.NewBuffer(nil)
|
||||
prog.Stdout = buf
|
||||
prog.Stderr = os.Stderr
|
||||
|
||||
68
vendor/helm.sh/helm/v3/pkg/ignore/doc.go
vendored
Normal file
68
vendor/helm.sh/helm/v3/pkg/ignore/doc.go
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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 ignore provides tools for writing ignore files (a la .gitignore).
|
||||
|
||||
This provides both an ignore parser and a file-aware processor.
|
||||
|
||||
The format of ignore files closely follows, but does not exactly match, the
|
||||
format for .gitignore files (https://git-scm.com/docs/gitignore).
|
||||
|
||||
The formatting rules are as follows:
|
||||
|
||||
- Parsing is line-by-line
|
||||
- Empty lines are ignored
|
||||
- Lines the begin with # (comments) will be ignored
|
||||
- Leading and trailing spaces are always ignored
|
||||
- Inline comments are NOT supported ('foo* # Any foo' does not contain a comment)
|
||||
- There is no support for multi-line patterns
|
||||
- Shell glob patterns are supported. See Go's "path/filepath".Match
|
||||
- If a pattern begins with a leading !, the match will be negated.
|
||||
- If a pattern begins with a leading /, only paths relatively rooted will match.
|
||||
- If the pattern ends with a trailing /, only directories will match
|
||||
- If a pattern contains no slashes, file basenames are tested (not paths)
|
||||
- The pattern sequence "**", while legal in a glob, will cause an error here
|
||||
(to indicate incompatibility with .gitignore).
|
||||
|
||||
Example:
|
||||
|
||||
# Match any file named foo.txt
|
||||
foo.txt
|
||||
|
||||
# Match any text file
|
||||
*.txt
|
||||
|
||||
# Match only directories named mydir
|
||||
mydir/
|
||||
|
||||
# Match only text files in the top-level directory
|
||||
/*.txt
|
||||
|
||||
# Match only the file foo.txt in the top-level directory
|
||||
/foo.txt
|
||||
|
||||
# Match any file named ab.txt, ac.txt, or ad.txt
|
||||
a[b-d].txt
|
||||
|
||||
Notable differences from .gitignore:
|
||||
- The '**' syntax is not supported.
|
||||
- The globbing library is Go's 'filepath.Match', not fnmatch(3)
|
||||
- Trailing spaces are always ignored (there is no supported escape sequence)
|
||||
- The evaluation of escape sequences has not been tested for compatibility
|
||||
- There is no support for '\!' as a special leading sequence.
|
||||
*/
|
||||
package ignore // import "helm.sh/helm/v3/pkg/ignore"
|
||||
67
vendor/helm.sh/helm/v3/pkg/kube/client.go
vendored
67
vendor/helm.sh/helm/v3/pkg/kube/client.go
vendored
@@ -37,6 +37,7 @@ import (
|
||||
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@@ -68,6 +69,14 @@ var ManagedFieldsManager string
|
||||
|
||||
// Client represents a client capable of communicating with the Kubernetes API.
|
||||
type Client struct {
|
||||
// Factory provides a minimal version of the kubectl Factory interface. If
|
||||
// you need the full Factory you can type switch to the full interface.
|
||||
// Since Kubernetes Go API does not provide backwards compatibility across
|
||||
// minor versions, this API does not follow Helm backwards compatibility.
|
||||
// Helm is exposing Kubernetes in this property and cannot guarantee this
|
||||
// will not change. The minimal interface only has the functions that Helm
|
||||
// needs. The smaller surface area of the interface means there is a lower
|
||||
// chance of it changing.
|
||||
Factory Factory
|
||||
Log func(string, ...interface{})
|
||||
// Namespace allows to bypass the kubeconfig file for the choice of the namespace
|
||||
@@ -337,13 +346,7 @@ func (c *Client) Build(reader io.Reader, validate bool) (ResourceList, error) {
|
||||
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)
|
||||
schema, err := c.Factory.Validator(validationDirective)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -363,13 +366,7 @@ func (c *Client) BuildTable(reader io.Reader, validate bool) (ResourceList, erro
|
||||
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)
|
||||
schema, err := c.Factory.Validator(validationDirective)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -456,7 +453,7 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err
|
||||
c.Log("Skipping delete of %q due to annotation [%s=%s]", info.Name, ResourcePolicyAnno, KeepPolicy)
|
||||
continue
|
||||
}
|
||||
if err := deleteResource(info); err != nil {
|
||||
if err := deleteResource(info, metav1.DeletePropagationBackground); err != nil {
|
||||
c.Log("Failed to delete %q, err: %s", info.ObjectName(), err)
|
||||
continue
|
||||
}
|
||||
@@ -465,17 +462,29 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Delete deletes Kubernetes resources specified in the resources list. It will
|
||||
// attempt to delete all resources even if one or more fail and collect any
|
||||
// errors. All successfully deleted items will be returned in the `Deleted`
|
||||
// ResourceList that is part of the result.
|
||||
// Delete deletes Kubernetes resources specified in the resources list with
|
||||
// background cascade deletion. It will attempt to delete all resources even
|
||||
// if one or more fail and collect any errors. All successfully deleted items
|
||||
// will be returned in the `Deleted` ResourceList that is part of the result.
|
||||
func (c *Client) Delete(resources ResourceList) (*Result, []error) {
|
||||
return rdelete(c, resources, metav1.DeletePropagationBackground)
|
||||
}
|
||||
|
||||
// Delete deletes Kubernetes resources specified in the resources list with
|
||||
// given deletion propagation policy. It will attempt to delete all resources even
|
||||
// if one or more fail and collect any errors. All successfully deleted items
|
||||
// will be returned in the `Deleted` ResourceList that is part of the result.
|
||||
func (c *Client) DeleteWithPropagationPolicy(resources ResourceList, policy metav1.DeletionPropagation) (*Result, []error) {
|
||||
return rdelete(c, resources, policy)
|
||||
}
|
||||
|
||||
func rdelete(c *Client, resources ResourceList, propagation metav1.DeletionPropagation) (*Result, []error) {
|
||||
var errs []error
|
||||
res := &Result{}
|
||||
mtx := sync.Mutex{}
|
||||
err := perform(resources, func(info *resource.Info) error {
|
||||
c.Log("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind)
|
||||
err := deleteResource(info)
|
||||
err := deleteResource(info, propagation)
|
||||
if err == nil || apierrors.IsNotFound(err) {
|
||||
if err != nil {
|
||||
c.Log("Ignoring delete failure for %q %s: %v", info.Name, info.Mapping.GroupVersionKind, err)
|
||||
@@ -492,10 +501,8 @@ func (c *Client) Delete(resources ResourceList) (*Result, []error) {
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
// Rewrite the message from "no objects visited" if that is what we got
|
||||
// back
|
||||
if err == ErrNoObjectsVisited {
|
||||
err = errors.New("object not found, skipping delete")
|
||||
if errors.Is(err, ErrNoObjectsVisited) {
|
||||
err = fmt.Errorf("object not found, skipping delete: %w", err)
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
@@ -532,6 +539,8 @@ func (c *Client) WatchUntilReady(resources ResourceList, timeout time.Duration)
|
||||
}
|
||||
|
||||
func perform(infos ResourceList, fn func(*resource.Info) error) error {
|
||||
var result error
|
||||
|
||||
if len(infos) == 0 {
|
||||
return ErrNoObjectsVisited
|
||||
}
|
||||
@@ -542,10 +551,11 @@ func perform(infos ResourceList, fn func(*resource.Info) error) error {
|
||||
for range infos {
|
||||
err := <-errs
|
||||
if err != nil {
|
||||
return err
|
||||
result = multierror.Append(result, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// getManagedFieldsManager returns the manager string. If one was set it will be returned.
|
||||
@@ -593,8 +603,7 @@ func createResource(info *resource.Info) error {
|
||||
return info.Refresh(obj, true)
|
||||
}
|
||||
|
||||
func deleteResource(info *resource.Info) error {
|
||||
policy := metav1.DeletePropagationBackground
|
||||
func deleteResource(info *resource.Info, policy metav1.DeletionPropagation) error {
|
||||
opts := &metav1.DeleteOptions{PropagationPolicy: &policy}
|
||||
_, err := resource.NewHelper(info.Client, info.Mapping).WithFieldManager(getManagedFieldsManager()).DeleteWithOptions(info.Namespace, info.Name, opts)
|
||||
return err
|
||||
@@ -763,7 +772,7 @@ func (c *Client) waitForJob(obj runtime.Object, name string) (bool, error) {
|
||||
if c.Type == batch.JobComplete && c.Status == "True" {
|
||||
return true, nil
|
||||
} else if c.Type == batch.JobFailed && c.Status == "True" {
|
||||
return true, errors.Errorf("job failed: %s", c.Reason)
|
||||
return true, errors.Errorf("job %s failed: %s", name, c.Reason)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
11
vendor/helm.sh/helm/v3/pkg/kube/factory.go
vendored
11
vendor/helm.sh/helm/v3/pkg/kube/factory.go
vendored
@@ -18,7 +18,6 @@ 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"
|
||||
@@ -27,6 +26,12 @@ import (
|
||||
|
||||
// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
|
||||
// of resources and different API sets.
|
||||
// This interface is a minimal copy of the kubectl Factory interface containing only the functions
|
||||
// needed by Helm. Since Kubernetes Go APIs, including interfaces, can change in any minor release
|
||||
// this interface is not covered by the Helm backwards compatibility guarantee. The reasons for the
|
||||
// minimal copy is that it does not include the full interface. Changes or additions to functions
|
||||
// Helm does not need are not impacted or exposed. This minimizes the impact of Kubernetes changes
|
||||
// being exposed.
|
||||
type Factory interface {
|
||||
// ToRawKubeConfigLoader return kubeconfig loader as-is
|
||||
ToRawKubeConfigLoader() clientcmd.ClientConfig
|
||||
@@ -42,7 +47,5 @@ type Factory interface {
|
||||
NewBuilder() *resource.Builder
|
||||
|
||||
// Returns a schema that can validate objects stored on disk.
|
||||
Validator(validationDirective string, verifier *resource.QueryParamVerifier) (validation.Schema, error)
|
||||
// OpenAPIGetter returns a getter for the openapi schema document
|
||||
OpenAPIGetter() discovery.OpenAPISchemaInterface
|
||||
Validator(validationDirective string) (validation.Schema, error)
|
||||
}
|
||||
|
||||
24
vendor/helm.sh/helm/v3/pkg/kube/fake/fake.go
vendored
24
vendor/helm.sh/helm/v3/pkg/kube/fake/fake.go
vendored
@@ -22,6 +22,7 @@ import (
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
|
||||
@@ -37,10 +38,12 @@ type FailingKubeClient struct {
|
||||
GetError error
|
||||
WaitError error
|
||||
DeleteError error
|
||||
DeleteWithPropagationError error
|
||||
WatchUntilReadyError error
|
||||
UpdateError error
|
||||
BuildError error
|
||||
BuildTableError error
|
||||
BuildDummy bool
|
||||
BuildUnstructuredError error
|
||||
WaitAndGetCompletedPodPhaseError error
|
||||
WaitDuration time.Duration
|
||||
@@ -116,6 +119,9 @@ func (f *FailingKubeClient) Build(r io.Reader, _ bool) (kube.ResourceList, error
|
||||
if f.BuildError != nil {
|
||||
return []*resource.Info{}, f.BuildError
|
||||
}
|
||||
if f.BuildDummy {
|
||||
return createDummyResourceList(), nil
|
||||
}
|
||||
return f.PrintingKubeClient.Build(r, false)
|
||||
}
|
||||
|
||||
@@ -134,3 +140,21 @@ func (f *FailingKubeClient) WaitAndGetCompletedPodPhase(s string, d time.Duratio
|
||||
}
|
||||
return f.PrintingKubeClient.WaitAndGetCompletedPodPhase(s, d)
|
||||
}
|
||||
|
||||
// DeleteWithPropagationPolicy returns the configured error if set or prints
|
||||
func (f *FailingKubeClient) DeleteWithPropagationPolicy(resources kube.ResourceList, policy metav1.DeletionPropagation) (*kube.Result, []error) {
|
||||
if f.DeleteWithPropagationError != nil {
|
||||
return nil, []error{f.DeleteWithPropagationError}
|
||||
}
|
||||
return f.PrintingKubeClient.DeleteWithPropagationPolicy(resources, policy)
|
||||
}
|
||||
|
||||
func createDummyResourceList() kube.ResourceList {
|
||||
var resInfo resource.Info
|
||||
resInfo.Name = "dummyName"
|
||||
resInfo.Namespace = "dummyNamespace"
|
||||
var resourceList kube.ResourceList
|
||||
resourceList.Append(&resInfo)
|
||||
return resourceList
|
||||
|
||||
}
|
||||
|
||||
14
vendor/helm.sh/helm/v3/pkg/kube/fake/printer.go
vendored
14
vendor/helm.sh/helm/v3/pkg/kube/fake/printer.go
vendored
@@ -22,6 +22,7 @@ import (
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
|
||||
@@ -48,7 +49,7 @@ func (p *PrintingKubeClient) Create(resources kube.ResourceList) (*kube.Result,
|
||||
return &kube.Result{Created: resources}, nil
|
||||
}
|
||||
|
||||
func (p *PrintingKubeClient) Get(resources kube.ResourceList, related bool) (map[string][]runtime.Object, error) {
|
||||
func (p *PrintingKubeClient) Get(resources kube.ResourceList, _ bool) (map[string][]runtime.Object, error) {
|
||||
_, err := io.Copy(p.Out, bufferize(resources))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -115,6 +116,17 @@ func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(_ string, _ time.Durati
|
||||
return v1.PodSucceeded, nil
|
||||
}
|
||||
|
||||
// DeleteWithPropagationPolicy implements KubeClient delete.
|
||||
//
|
||||
// It only prints out the content to be deleted.
|
||||
func (p *PrintingKubeClient) DeleteWithPropagationPolicy(resources kube.ResourceList, _ metav1.DeletionPropagation) (*kube.Result, []error) {
|
||||
_, err := io.Copy(p.Out, bufferize(resources))
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
return &kube.Result{Deleted: resources}, nil
|
||||
}
|
||||
|
||||
func bufferize(resources kube.ResourceList) io.Reader {
|
||||
var builder strings.Builder
|
||||
for _, info := range resources {
|
||||
|
||||
10
vendor/helm.sh/helm/v3/pkg/kube/interface.go
vendored
10
vendor/helm.sh/helm/v3/pkg/kube/interface.go
vendored
@@ -21,6 +21,7 @@ import (
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
@@ -79,6 +80,14 @@ type InterfaceExt interface {
|
||||
WaitForDelete(resources ResourceList, timeout time.Duration) error
|
||||
}
|
||||
|
||||
// InterfaceDeletionPropagation is introduced to avoid breaking backwards compatibility for Interface implementers.
|
||||
//
|
||||
// TODO Helm 4: Remove InterfaceDeletionPropagation and integrate its method(s) into the Interface.
|
||||
type InterfaceDeletionPropagation interface {
|
||||
// Delete destroys one or more resources. The deletion propagation is handled as per the given deletion propagation value.
|
||||
DeleteWithPropagationPolicy(resources ResourceList, policy metav1.DeletionPropagation) (*Result, []error)
|
||||
}
|
||||
|
||||
// InterfaceResources is introduced to avoid breaking backwards compatibility for Interface implementers.
|
||||
//
|
||||
// TODO Helm 4: Remove InterfaceResources and integrate its method(s) into the Interface.
|
||||
@@ -103,4 +112,5 @@ type InterfaceResources interface {
|
||||
|
||||
var _ Interface = (*Client)(nil)
|
||||
var _ InterfaceExt = (*Client)(nil)
|
||||
var _ InterfaceDeletionPropagation = (*Client)(nil)
|
||||
var _ InterfaceResources = (*Client)(nil)
|
||||
|
||||
108
vendor/helm.sh/helm/v3/pkg/kube/ready.go
vendored
108
vendor/helm.sh/helm/v3/pkg/kube/ready.go
vendored
@@ -18,6 +18,7 @@ package kube // import "helm.sh/helm/v3/pkg/kube"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||
@@ -83,19 +84,12 @@ type ReadyChecker struct {
|
||||
|
||||
// IsReady checks if v is ready. It supports checking readiness for pods,
|
||||
// deployments, persistent volume claims, services, daemon sets, custom
|
||||
// resource definitions, stateful sets, replication controllers, and replica
|
||||
// sets. All other resource kinds are always considered ready.
|
||||
// resource definitions, stateful sets, replication controllers, jobs (optional),
|
||||
// and replica sets. All other resource kinds are always considered ready.
|
||||
//
|
||||
// IsReady will fetch the latest state of the object from the server prior to
|
||||
// performing readiness checks, and it will return any error encountered.
|
||||
func (c *ReadyChecker) IsReady(ctx context.Context, v *resource.Info) (bool, error) {
|
||||
var (
|
||||
// This defaults to true, otherwise we get to a point where
|
||||
// things will always return false unless one of the objects
|
||||
// that manages pods has been hit
|
||||
ok = true
|
||||
err error
|
||||
)
|
||||
switch value := AsVersioned(v).(type) {
|
||||
case *corev1.Pod:
|
||||
pod, err := c.client.CoreV1().Pods(v.Namespace).Get(ctx, v.Name, metav1.GetOptions{})
|
||||
@@ -105,9 +99,11 @@ func (c *ReadyChecker) IsReady(ctx context.Context, v *resource.Info) (bool, err
|
||||
case *batchv1.Job:
|
||||
if c.checkJobs {
|
||||
job, err := c.client.BatchV1().Jobs(v.Namespace).Get(ctx, v.Name, metav1.GetOptions{})
|
||||
if err != nil || !c.jobReady(job) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ready, err := c.jobReady(job)
|
||||
return ready, err
|
||||
}
|
||||
case *appsv1.Deployment, *appsv1beta1.Deployment, *appsv1beta2.Deployment, *extensionsv1beta1.Deployment:
|
||||
currentDeployment, err := c.client.AppsV1().Deployments(v.Namespace).Get(ctx, v.Name, metav1.GetOptions{})
|
||||
@@ -180,11 +176,30 @@ func (c *ReadyChecker) IsReady(ctx context.Context, v *resource.Info) (bool, err
|
||||
if !c.statefulSetReady(sts) {
|
||||
return false, nil
|
||||
}
|
||||
case *corev1.ReplicationController, *extensionsv1beta1.ReplicaSet, *appsv1beta2.ReplicaSet, *appsv1.ReplicaSet:
|
||||
ok, err = c.podsReadyForObject(ctx, v.Namespace, value)
|
||||
}
|
||||
if !ok || err != nil {
|
||||
return false, err
|
||||
case *corev1.ReplicationController:
|
||||
rc, err := c.client.CoreV1().ReplicationControllers(v.Namespace).Get(ctx, v.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !c.replicationControllerReady(rc) {
|
||||
return false, nil
|
||||
}
|
||||
ready, err := c.podsReadyForObject(ctx, v.Namespace, value)
|
||||
if !ready || err != nil {
|
||||
return false, err
|
||||
}
|
||||
case *extensionsv1beta1.ReplicaSet, *appsv1beta2.ReplicaSet, *appsv1.ReplicaSet:
|
||||
rs, err := c.client.AppsV1().ReplicaSets(v.Namespace).Get(ctx, v.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !c.replicaSetReady(rs) {
|
||||
return false, nil
|
||||
}
|
||||
ready, err := c.podsReadyForObject(ctx, v.Namespace, value)
|
||||
if !ready || err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -222,16 +237,17 @@ func (c *ReadyChecker) isPodReady(pod *corev1.Pod) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *ReadyChecker) jobReady(job *batchv1.Job) bool {
|
||||
func (c *ReadyChecker) jobReady(job *batchv1.Job) (bool, error) {
|
||||
if job.Status.Failed > *job.Spec.BackoffLimit {
|
||||
c.log("Job is failed: %s/%s", job.GetNamespace(), job.GetName())
|
||||
return false
|
||||
// If a job is failed, it can't recover, so throw an error
|
||||
return false, fmt.Errorf("job is failed: %s/%s", job.GetNamespace(), job.GetName())
|
||||
}
|
||||
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
|
||||
return false, nil
|
||||
}
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (c *ReadyChecker) serviceReady(s *corev1.Service) bool {
|
||||
@@ -272,6 +288,16 @@ func (c *ReadyChecker) volumeReady(v *corev1.PersistentVolumeClaim) bool {
|
||||
}
|
||||
|
||||
func (c *ReadyChecker) deploymentReady(rs *appsv1.ReplicaSet, dep *appsv1.Deployment) bool {
|
||||
// Verify the replicaset readiness
|
||||
if !c.replicaSetReady(rs) {
|
||||
return false
|
||||
}
|
||||
// Verify the generation observed by the deployment controller matches the spec generation
|
||||
if dep.Status.ObservedGeneration != dep.ObjectMeta.Generation {
|
||||
c.log("Deployment is not ready: %s/%s. observedGeneration (%d) does not match spec generation (%d).", dep.Namespace, dep.Name, dep.Status.ObservedGeneration, dep.ObjectMeta.Generation)
|
||||
return false
|
||||
}
|
||||
|
||||
expectedReady := *dep.Spec.Replicas - deploymentutil.MaxUnavailable(*dep)
|
||||
if !(rs.Status.ReadyReplicas >= expectedReady) {
|
||||
c.log("Deployment is not ready: %s/%s. %d out of %d expected pods are ready", dep.Namespace, dep.Name, rs.Status.ReadyReplicas, expectedReady)
|
||||
@@ -281,6 +307,12 @@ func (c *ReadyChecker) deploymentReady(rs *appsv1.ReplicaSet, dep *appsv1.Deploy
|
||||
}
|
||||
|
||||
func (c *ReadyChecker) daemonSetReady(ds *appsv1.DaemonSet) bool {
|
||||
// Verify the generation observed by the daemonSet controller matches the spec generation
|
||||
if ds.Status.ObservedGeneration != ds.ObjectMeta.Generation {
|
||||
c.log("DaemonSet is not ready: %s/%s. observedGeneration (%d) does not match spec generation (%d).", ds.Namespace, ds.Name, ds.Status.ObservedGeneration, ds.ObjectMeta.Generation)
|
||||
return false
|
||||
}
|
||||
|
||||
// If the update strategy is not a rolling update, there will be nothing to wait for
|
||||
if ds.Spec.UpdateStrategy.Type != appsv1.RollingUpdateDaemonSetStrategyType {
|
||||
return true
|
||||
@@ -351,22 +383,22 @@ func (c *ReadyChecker) crdReady(crd apiextv1.CustomResourceDefinition) bool {
|
||||
}
|
||||
|
||||
func (c *ReadyChecker) statefulSetReady(sts *appsv1.StatefulSet) bool {
|
||||
// Verify the generation observed by the statefulSet controller matches the spec generation
|
||||
if sts.Status.ObservedGeneration != sts.ObjectMeta.Generation {
|
||||
c.log("StatefulSet is not ready: %s/%s. observedGeneration (%d) does not match spec generation (%d).", sts.Namespace, sts.Name, sts.Status.ObservedGeneration, sts.ObjectMeta.Generation)
|
||||
return false
|
||||
}
|
||||
|
||||
// If the update strategy is not a rolling update, there will be nothing to wait for
|
||||
if sts.Spec.UpdateStrategy.Type != appsv1.RollingUpdateStatefulSetStrategyType {
|
||||
c.log("StatefulSet skipped ready check: %s/%s. updateStrategy is %v", sts.Namespace, sts.Name, sts.Spec.UpdateStrategy.Type)
|
||||
return true
|
||||
}
|
||||
|
||||
// Make sure the status is up-to-date with the StatefulSet changes
|
||||
if sts.Status.ObservedGeneration < sts.Generation {
|
||||
c.log("StatefulSet is not ready: %s/%s. update has not yet been observed", sts.Namespace, sts.Name)
|
||||
return false
|
||||
}
|
||||
|
||||
// Dereference all the pointers because StatefulSets like them
|
||||
var partition int
|
||||
// 1 is the default for replicas if not set
|
||||
var replicas = 1
|
||||
replicas := 1
|
||||
// For some reason, even if the update strategy is a rolling update, the
|
||||
// actual rollingUpdate field can be nil. If it is, we can safely assume
|
||||
// there is no partition value
|
||||
@@ -393,8 +425,10 @@ func (c *ReadyChecker) statefulSetReady(sts *appsv1.StatefulSet) bool {
|
||||
c.log("StatefulSet is not ready: %s/%s. %d out of %d expected pods are ready", sts.Namespace, sts.Name, sts.Status.ReadyReplicas, replicas)
|
||||
return false
|
||||
}
|
||||
|
||||
if sts.Status.CurrentRevision != sts.Status.UpdateRevision {
|
||||
// This check only makes sense when all partitions are being upgraded otherwise during a
|
||||
// partioned rolling upgrade, this condition will never evaluate to true, leading to
|
||||
// error.
|
||||
if partition == 0 && sts.Status.CurrentRevision != sts.Status.UpdateRevision {
|
||||
c.log("StatefulSet is not ready: %s/%s. currentRevision %s does not yet match updateRevision %s", sts.Namespace, sts.Name, sts.Status.CurrentRevision, sts.Status.UpdateRevision)
|
||||
return false
|
||||
}
|
||||
@@ -403,6 +437,24 @@ func (c *ReadyChecker) statefulSetReady(sts *appsv1.StatefulSet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ReadyChecker) replicationControllerReady(rc *corev1.ReplicationController) bool {
|
||||
// Verify the generation observed by the replicationController controller matches the spec generation
|
||||
if rc.Status.ObservedGeneration != rc.ObjectMeta.Generation {
|
||||
c.log("ReplicationController is not ready: %s/%s. observedGeneration (%d) does not match spec generation (%d).", rc.Namespace, rc.Name, rc.Status.ObservedGeneration, rc.ObjectMeta.Generation)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ReadyChecker) replicaSetReady(rs *appsv1.ReplicaSet) bool {
|
||||
// Verify the generation observed by the replicaSet controller matches the spec generation
|
||||
if rs.Status.ObservedGeneration != rs.ObjectMeta.Generation {
|
||||
c.log("ReplicaSet is not ready: %s/%s. observedGeneration (%d) does not match spec generation (%d).", rs.Namespace, rs.Name, rs.Status.ObservedGeneration, rs.ObjectMeta.Generation)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getPods(ctx context.Context, client kubernetes.Interface, namespace, selector string) ([]corev1.Pod, error) {
|
||||
list, err := client.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{
|
||||
LabelSelector: selector,
|
||||
|
||||
@@ -22,5 +22,6 @@ const ResourcePolicyAnno = "helm.sh/resource-policy"
|
||||
// KeepPolicy is the resource policy type for keep
|
||||
//
|
||||
// This resource policy type allows resources to skip being deleted
|
||||
// during an uninstallRelease action.
|
||||
//
|
||||
// during an uninstallRelease action.
|
||||
const KeepPolicy = "keep"
|
||||
|
||||
8
vendor/helm.sh/helm/v3/pkg/kube/wait.go
vendored
8
vendor/helm.sh/helm/v3/pkg/kube/wait.go
vendored
@@ -50,7 +50,7 @@ func (w *waiter) waitForResources(created ResourceList) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), w.timeout)
|
||||
defer cancel()
|
||||
|
||||
return wait.PollImmediateUntil(2*time.Second, func() (bool, error) {
|
||||
return wait.PollUntilContextCancel(ctx, 2*time.Second, true, func(ctx context.Context) (bool, error) {
|
||||
for _, v := range created {
|
||||
ready, err := w.c.IsReady(ctx, v)
|
||||
if !ready || err != nil {
|
||||
@@ -58,7 +58,7 @@ func (w *waiter) waitForResources(created ResourceList) error {
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}, ctx.Done())
|
||||
})
|
||||
}
|
||||
|
||||
// waitForDeletedResources polls to check if all the resources are deleted or a timeout is reached
|
||||
@@ -68,7 +68,7 @@ func (w *waiter) waitForDeletedResources(deleted ResourceList) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), w.timeout)
|
||||
defer cancel()
|
||||
|
||||
return wait.PollImmediateUntil(2*time.Second, func() (bool, error) {
|
||||
return wait.PollUntilContextCancel(ctx, 2*time.Second, true, func(ctx context.Context) (bool, error) {
|
||||
for _, v := range deleted {
|
||||
err := v.Get()
|
||||
if err == nil || !apierrors.IsNotFound(err) {
|
||||
@@ -76,7 +76,7 @@ func (w *waiter) waitForDeletedResources(deleted ResourceList) error {
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}, ctx.Done())
|
||||
})
|
||||
}
|
||||
|
||||
// SelectorsForObject returns the pod label selector for a given object
|
||||
|
||||
10
vendor/helm.sh/helm/v3/pkg/lint/lint.go
vendored
10
vendor/helm.sh/helm/v3/pkg/lint/lint.go
vendored
@@ -19,19 +19,25 @@ package lint // import "helm.sh/helm/v3/pkg/lint"
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/lint/rules"
|
||||
"helm.sh/helm/v3/pkg/lint/support"
|
||||
)
|
||||
|
||||
// All runs all of the available linters on the given base directory.
|
||||
func All(basedir string, values map[string]interface{}, namespace string, strict bool) support.Linter {
|
||||
func All(basedir string, values map[string]interface{}, namespace string, _ bool) support.Linter {
|
||||
return AllWithKubeVersion(basedir, values, namespace, nil)
|
||||
}
|
||||
|
||||
// AllWithKubeVersion runs all the available linters on the given base directory, allowing to specify the kubernetes version.
|
||||
func AllWithKubeVersion(basedir string, values map[string]interface{}, namespace string, kubeVersion *chartutil.KubeVersion) support.Linter {
|
||||
// Using abs path to get directory context
|
||||
chartDir, _ := filepath.Abs(basedir)
|
||||
|
||||
linter := support.Linter{ChartDir: chartDir}
|
||||
rules.Chartfile(&linter)
|
||||
rules.ValuesWithOverrides(&linter, values)
|
||||
rules.Templates(&linter, values, namespace, strict)
|
||||
rules.TemplatesWithKubeVersion(&linter, values, namespace, kubeVersion)
|
||||
rules.Dependencies(&linter)
|
||||
return linter
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package rules // import "helm.sh/helm/v3/pkg/lint/rules"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -107,6 +106,10 @@ func validateChartName(cf *chart.Metadata) error {
|
||||
if cf.Name == "" {
|
||||
return errors.New("name is required")
|
||||
}
|
||||
name := filepath.Base(cf.Name)
|
||||
if name != cf.Name {
|
||||
return fmt.Errorf("chart name %q is invalid", cf.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -200,7 +203,7 @@ func validateChartType(cf *chart.Metadata) error {
|
||||
// in a generic form of a map[string]interface{}, so that the type
|
||||
// of the values can be checked
|
||||
func loadChartFileForTypeCheck(filename string) (map[string]interface{}, error) {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
b, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ func Dependencies(linter *support.Linter) {
|
||||
}
|
||||
|
||||
linter.RunLinterRule(support.ErrorSev, linter.ChartDir, validateDependencyInMetadata(c))
|
||||
linter.RunLinterRule(support.ErrorSev, linter.ChartDir, validateDependenciesUnique(c))
|
||||
linter.RunLinterRule(support.WarningSev, linter.ChartDir, validateDependencyInChartsDir(c))
|
||||
}
|
||||
|
||||
@@ -80,3 +81,23 @@ func validateDependencyInMetadata(c *chart.Chart) (err error) {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func validateDependenciesUnique(c *chart.Chart) (err error) {
|
||||
dependencies := map[string]*chart.Dependency{}
|
||||
shadowing := []string{}
|
||||
|
||||
for _, dep := range c.Metadata.Dependencies {
|
||||
key := dep.Name
|
||||
if dep.Alias != "" {
|
||||
key = dep.Alias
|
||||
}
|
||||
if dependencies[key] != nil {
|
||||
shadowing = append(shadowing, key)
|
||||
}
|
||||
dependencies[key] = dep
|
||||
}
|
||||
if len(shadowing) > 0 {
|
||||
err = fmt.Errorf("multiple dependencies with name or alias: %s", strings.Join(shadowing, ","))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/endpoints/deprecation"
|
||||
kscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -45,7 +47,7 @@ func (e deprecatedAPIError) Error() string {
|
||||
return msg
|
||||
}
|
||||
|
||||
func validateNoDeprecations(resource *K8sYamlStruct) error {
|
||||
func validateNoDeprecations(resource *K8sYamlStruct, kubeVersion *chartutil.KubeVersion) error {
|
||||
// if `resource` does not have an APIVersion or Kind, we cannot test it for deprecation
|
||||
if resource.APIVersion == "" {
|
||||
return nil
|
||||
@@ -54,6 +56,14 @@ func validateNoDeprecations(resource *K8sYamlStruct) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
majorVersion := k8sVersionMajor
|
||||
minorVersion := k8sVersionMinor
|
||||
|
||||
if kubeVersion != nil {
|
||||
majorVersion = kubeVersion.Major
|
||||
minorVersion = kubeVersion.Minor
|
||||
}
|
||||
|
||||
runtimeObject, err := resourceToRuntimeObject(resource)
|
||||
if err != nil {
|
||||
// do not error for non-kubernetes resources
|
||||
@@ -62,11 +72,12 @@ func validateNoDeprecations(resource *K8sYamlStruct) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
maj, err := strconv.Atoi(k8sVersionMajor)
|
||||
|
||||
maj, err := strconv.Atoi(majorVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
min, err := strconv.Atoi(k8sVersionMinor)
|
||||
min, err := strconv.Atoi(minorVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -45,7 +45,12 @@ var (
|
||||
)
|
||||
|
||||
// Templates lints the templates in the Linter.
|
||||
func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool) {
|
||||
func Templates(linter *support.Linter, values map[string]interface{}, namespace string, _ bool) {
|
||||
TemplatesWithKubeVersion(linter, values, namespace, nil)
|
||||
}
|
||||
|
||||
// TemplatesWithKubeVersion lints the templates in the Linter, allowing to specify the kubernetes version.
|
||||
func TemplatesWithKubeVersion(linter *support.Linter, values map[string]interface{}, namespace string, kubeVersion *chartutil.KubeVersion) {
|
||||
fpath := "templates/"
|
||||
templatesPath := filepath.Join(linter.ChartDir, fpath)
|
||||
|
||||
@@ -70,9 +75,14 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace
|
||||
Namespace: namespace,
|
||||
}
|
||||
|
||||
caps := chartutil.DefaultCapabilities.Copy()
|
||||
if kubeVersion != nil {
|
||||
caps.KubeVersion = *kubeVersion
|
||||
}
|
||||
|
||||
// lint ignores import-values
|
||||
// See https://github.com/helm/helm/issues/9658
|
||||
if err := chartutil.ProcessDependencies(chart, values); err != nil {
|
||||
if err := chartutil.ProcessDependenciesWithMerge(chart, values); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,7 +90,8 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
valuesToRender, err := chartutil.ToRenderValues(chart, cvals, options, nil)
|
||||
|
||||
valuesToRender, err := chartutil.ToRenderValues(chart, cvals, options, caps)
|
||||
if err != nil {
|
||||
linter.RunLinterRule(support.ErrorSev, fpath, err)
|
||||
return
|
||||
@@ -141,15 +152,16 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace
|
||||
break
|
||||
}
|
||||
|
||||
// If YAML linting fails, we sill progress. So we don't capture the returned state
|
||||
// on this linter run.
|
||||
linter.RunLinterRule(support.ErrorSev, fpath, validateYamlContent(err))
|
||||
|
||||
// If YAML linting fails here, it will always fail in the next block as well, so we should return here.
|
||||
// fix https://github.com/helm/helm/issues/11391
|
||||
if !linter.RunLinterRule(support.ErrorSev, fpath, validateYamlContent(err)) {
|
||||
return
|
||||
}
|
||||
if yamlStruct != nil {
|
||||
// NOTE: set to warnings to allow users to support out-of-date kubernetes
|
||||
// Refs https://github.com/helm/helm/issues/8596
|
||||
linter.RunLinterRule(support.WarningSev, fpath, validateMetadataName(yamlStruct))
|
||||
linter.RunLinterRule(support.WarningSev, fpath, validateNoDeprecations(yamlStruct))
|
||||
linter.RunLinterRule(support.WarningSev, fpath, validateNoDeprecations(yamlStruct, kubeVersion))
|
||||
|
||||
linter.RunLinterRule(support.ErrorSev, fpath, validateMatchSelector(yamlStruct, renderedContent))
|
||||
linter.RunLinterRule(support.ErrorSev, fpath, validateListAnnotations(yamlStruct, renderedContent))
|
||||
@@ -187,10 +199,10 @@ func validateTopIndentLevel(content string) error {
|
||||
|
||||
// Validation functions
|
||||
func validateTemplatesDir(templatesPath string) error {
|
||||
if fi, err := os.Stat(templatesPath); err != nil {
|
||||
return errors.New("directory not found")
|
||||
} else if !fi.IsDir() {
|
||||
return errors.New("not a directory")
|
||||
if fi, err := os.Stat(templatesPath); err == nil {
|
||||
if !fi.IsDir() {
|
||||
return errors.New("not a directory")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package rules
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -76,7 +75,7 @@ func validateValuesFile(valuesPath string, overrides map[string]interface{}) err
|
||||
|
||||
ext := filepath.Ext(valuesPath)
|
||||
schemaPath := valuesPath[:len(valuesPath)-len(ext)] + ".schema.json"
|
||||
schema, err := ioutil.ReadFile(schemaPath)
|
||||
schema, err := os.ReadFile(schemaPath)
|
||||
if len(schema) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*Package support contains tools for linting charts.
|
||||
/*
|
||||
Package support contains tools for linting charts.
|
||||
|
||||
Linting is the process of testing charts for errors or warnings regarding
|
||||
formatting, compilation, or standards compliance.
|
||||
|
||||
20
vendor/helm.sh/helm/v3/pkg/plugin/plugin.go
vendored
20
vendor/helm.sh/helm/v3/pkg/plugin/plugin.go
vendored
@@ -17,7 +17,6 @@ package plugin // import "helm.sh/helm/v3/pkg/plugin"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -122,10 +121,10 @@ func getPlatformCommand(cmds []PlatformCommand) []string {
|
||||
eq := strings.EqualFold
|
||||
for _, c := range cmds {
|
||||
if eq(c.OperatingSystem, runtime.GOOS) {
|
||||
command = strings.Split(os.ExpandEnv(c.Command), " ")
|
||||
command = strings.Split(c.Command, " ")
|
||||
}
|
||||
if eq(c.OperatingSystem, runtime.GOOS) && eq(c.Architecture, runtime.GOARCH) {
|
||||
return strings.Split(os.ExpandEnv(c.Command), " ")
|
||||
return strings.Split(c.Command, " ")
|
||||
}
|
||||
}
|
||||
return command
|
||||
@@ -149,16 +148,19 @@ func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string, error) {
|
||||
parts = getPlatformCommand(p.Metadata.PlatformCommand)
|
||||
}
|
||||
if platCmdLen == 0 || parts == nil {
|
||||
parts = strings.Split(os.ExpandEnv(p.Metadata.Command), " ")
|
||||
parts = strings.Split(p.Metadata.Command, " ")
|
||||
}
|
||||
if len(parts) == 0 || parts[0] == "" {
|
||||
return "", nil, fmt.Errorf("no plugin command is applicable")
|
||||
}
|
||||
|
||||
main := parts[0]
|
||||
main := os.ExpandEnv(parts[0])
|
||||
baseArgs := []string{}
|
||||
if len(parts) > 1 {
|
||||
baseArgs = parts[1:]
|
||||
for _, cmdpart := range parts[1:] {
|
||||
cmdexp := os.ExpandEnv(cmdpart)
|
||||
baseArgs = append(baseArgs, cmdexp)
|
||||
}
|
||||
}
|
||||
if !p.Metadata.IgnoreFlags {
|
||||
baseArgs = append(baseArgs, extraArgs...)
|
||||
@@ -173,6 +175,10 @@ var validPluginName = regexp.MustCompile("^[A-Za-z0-9_-]+$")
|
||||
|
||||
// validatePluginData validates a plugin's YAML data.
|
||||
func validatePluginData(plug *Plugin, filepath string) error {
|
||||
// When metadata section missing, initialize with no data
|
||||
if plug.Metadata == nil {
|
||||
plug.Metadata = &Metadata{}
|
||||
}
|
||||
if !validPluginName.MatchString(plug.Metadata.Name) {
|
||||
return fmt.Errorf("invalid plugin name at %q", filepath)
|
||||
}
|
||||
@@ -216,7 +222,7 @@ func detectDuplicates(plugs []*Plugin) error {
|
||||
// LoadDir loads a plugin from the given directory.
|
||||
func LoadDir(dirname string) (*Plugin, error) {
|
||||
pluginfile := filepath.Join(dirname, PluginFileName)
|
||||
data, err := ioutil.ReadFile(pluginfile)
|
||||
data, err := os.ReadFile(pluginfile)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read plugin at %q", pluginfile)
|
||||
}
|
||||
|
||||
3
vendor/helm.sh/helm/v3/pkg/provenance/doc.go
vendored
3
vendor/helm.sh/helm/v3/pkg/provenance/doc.go
vendored
@@ -13,7 +13,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*Package provenance provides tools for establishing the authenticity of a chart.
|
||||
/*
|
||||
Package provenance provides tools for establishing the authenticity of a chart.
|
||||
|
||||
In Helm, provenance is established via several factors. The primary factor is the
|
||||
cryptographic signature of a chart. Chart authors may sign charts, which in turn
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"crypto"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -42,9 +41,13 @@ var defaultPGPConfig = packet.Config{
|
||||
// SumCollection represents a collection of file and image checksums.
|
||||
//
|
||||
// Files are of the form:
|
||||
//
|
||||
// FILENAME: "sha256:SUM"
|
||||
//
|
||||
// Images are of the form:
|
||||
//
|
||||
// "IMAGE:TAG": "sha256:SUM"
|
||||
//
|
||||
// Docker optionally supports sha512, and if this is the case, the hash marker
|
||||
// will be 'sha512' instead of 'sha256'.
|
||||
type SumCollection struct {
|
||||
@@ -293,7 +296,7 @@ func (s *Signatory) Verify(chartpath, sigpath string) (*Verification, error) {
|
||||
}
|
||||
|
||||
func (s *Signatory) decodeSignature(filename string) (*clearsign.Block, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
76
vendor/helm.sh/helm/v3/pkg/pusher/ocipusher.go
vendored
76
vendor/helm.sh/helm/v3/pkg/pusher/ocipusher.go
vendored
@@ -17,13 +17,16 @@ package pusher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/internal/tlsutil"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
)
|
||||
@@ -59,8 +62,15 @@ func (pusher *OCIPusher) push(chartRef, href string) error {
|
||||
}
|
||||
|
||||
client := pusher.opts.registryClient
|
||||
if client == nil {
|
||||
c, err := pusher.newRegistryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client = c
|
||||
}
|
||||
|
||||
chartBytes, err := ioutil.ReadFile(chartRef)
|
||||
chartBytes, err := os.ReadFile(chartRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -68,7 +78,7 @@ func (pusher *OCIPusher) push(chartRef, href string) error {
|
||||
var pushOpts []registry.PushOption
|
||||
provRef := fmt.Sprintf("%s.prov", chartRef)
|
||||
if _, err := os.Stat(provRef); err == nil {
|
||||
provBytes, err := ioutil.ReadFile(provRef)
|
||||
provBytes, err := os.ReadFile(provRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -85,18 +95,7 @@ func (pusher *OCIPusher) push(chartRef, href string) error {
|
||||
|
||||
// NewOCIPusher constructs a valid OCI client as a Pusher
|
||||
func NewOCIPusher(ops ...Option) (Pusher, error) {
|
||||
registryClient, err := registry.NewClient(
|
||||
registry.ClientOptEnableCache(true),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := OCIPusher{
|
||||
opts: options{
|
||||
registryClient: registryClient,
|
||||
},
|
||||
}
|
||||
var client OCIPusher
|
||||
|
||||
for _, opt := range ops {
|
||||
opt(&client.opts)
|
||||
@@ -104,3 +103,50 @@ func NewOCIPusher(ops ...Option) (Pusher, error) {
|
||||
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
func (pusher *OCIPusher) newRegistryClient() (*registry.Client, error) {
|
||||
if (pusher.opts.certFile != "" && pusher.opts.keyFile != "") || pusher.opts.caFile != "" || pusher.opts.insecureSkipTLSverify {
|
||||
tlsConf, err := tlsutil.NewClientTLS(pusher.opts.certFile, pusher.opts.keyFile, pusher.opts.caFile, pusher.opts.insecureSkipTLSverify)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't create TLS config for client")
|
||||
}
|
||||
|
||||
registryClient, err := registry.NewClient(
|
||||
registry.ClientOptHTTPClient(&http.Client{
|
||||
// From https://github.com/google/go-containerregistry/blob/31786c6cbb82d6ec4fb8eb79cd9387905130534e/pkg/v1/remote/options.go#L87
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
// By default we wrap the transport in retries, so reduce the
|
||||
// default dial timeout to 5s to avoid 5x 30s of connection
|
||||
// timeouts when doing the "ping" on certain http registries.
|
||||
Timeout: 5 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: tlsConf,
|
||||
},
|
||||
}),
|
||||
registry.ClientOptEnableCache(true),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registryClient, nil
|
||||
}
|
||||
|
||||
opts := []registry.ClientOption{registry.ClientOptEnableCache(true)}
|
||||
if pusher.opts.plainHTTP {
|
||||
opts = append(opts, registry.ClientOptPlainHTTP())
|
||||
}
|
||||
|
||||
registryClient, err := registry.NewClient(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registryClient, nil
|
||||
}
|
||||
|
||||
31
vendor/helm.sh/helm/v3/pkg/pusher/pusher.go
vendored
31
vendor/helm.sh/helm/v3/pkg/pusher/pusher.go
vendored
@@ -27,7 +27,12 @@ import (
|
||||
//
|
||||
// Pushers may or may not ignore these parameters as they are passed in.
|
||||
type options struct {
|
||||
registryClient *registry.Client
|
||||
registryClient *registry.Client
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
insecureSkipTLSverify bool
|
||||
plainHTTP bool
|
||||
}
|
||||
|
||||
// Option allows specifying various settings configurable by the user for overriding the defaults
|
||||
@@ -41,6 +46,28 @@ func WithRegistryClient(client *registry.Client) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithTLSClientConfig sets the client auth with the provided credentials.
|
||||
func WithTLSClientConfig(certFile, keyFile, caFile string) Option {
|
||||
return func(opts *options) {
|
||||
opts.certFile = certFile
|
||||
opts.keyFile = keyFile
|
||||
opts.caFile = caFile
|
||||
}
|
||||
}
|
||||
|
||||
// WithInsecureSkipTLSVerify determines if a TLS Certificate will be checked
|
||||
func WithInsecureSkipTLSVerify(insecureSkipTLSVerify bool) Option {
|
||||
return func(opts *options) {
|
||||
opts.insecureSkipTLSverify = insecureSkipTLSVerify
|
||||
}
|
||||
}
|
||||
|
||||
func WithPlainHTTP(plainHTTP bool) Option {
|
||||
return func(opts *options) {
|
||||
opts.plainHTTP = plainHTTP
|
||||
}
|
||||
}
|
||||
|
||||
// Pusher is an interface to support upload to the specified URL.
|
||||
type Pusher interface {
|
||||
// Push file content by url string
|
||||
@@ -89,7 +116,7 @@ var ociProvider = Provider{
|
||||
|
||||
// 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 {
|
||||
func All(_ *cli.EnvSettings) Providers {
|
||||
result := Providers{ociProvider}
|
||||
return result
|
||||
}
|
||||
|
||||
124
vendor/helm.sh/helm/v3/pkg/registry/client.go
vendored
124
vendor/helm.sh/helm/v3/pkg/registry/client.go
vendored
@@ -21,7 +21,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -60,7 +59,9 @@ type (
|
||||
out io.Writer
|
||||
authorizer auth.Client
|
||||
registryAuthorizer *registryauth.Client
|
||||
resolver remotes.Resolver
|
||||
resolver func(ref registry.Reference) (remotes.Resolver, error)
|
||||
httpClient *http.Client
|
||||
plainHTTP bool
|
||||
}
|
||||
|
||||
// ClientOption allows specifying various settings configurable by the user for overriding the defaults
|
||||
@@ -71,7 +72,7 @@ type (
|
||||
// NewClient returns a new registry client with config
|
||||
func NewClient(options ...ClientOption) (*Client, error) {
|
||||
client := &Client{
|
||||
out: ioutil.Discard,
|
||||
out: io.Discard,
|
||||
}
|
||||
for _, option := range options {
|
||||
option(client)
|
||||
@@ -86,15 +87,29 @@ func NewClient(options ...ClientOption) (*Client, error) {
|
||||
}
|
||||
client.authorizer = authClient
|
||||
}
|
||||
if client.resolver == nil {
|
||||
|
||||
resolverFn := client.resolver // copy for avoiding recursive call
|
||||
client.resolver = func(ref registry.Reference) (remotes.Resolver, error) {
|
||||
if resolverFn != nil {
|
||||
// validate if the resolverFn returns a valid resolver
|
||||
if resolver, err := resolverFn(ref); resolver != nil && err == nil {
|
||||
return resolver, nil
|
||||
}
|
||||
}
|
||||
headers := http.Header{}
|
||||
headers.Set("User-Agent", version.GetUserAgent())
|
||||
opts := []auth.ResolverOption{auth.WithResolverHeaders(headers)}
|
||||
if client.httpClient != nil {
|
||||
opts = append(opts, auth.WithResolverClient(client.httpClient))
|
||||
}
|
||||
if client.plainHTTP {
|
||||
opts = append(opts, auth.WithResolverPlainHTTP())
|
||||
}
|
||||
resolver, err := client.authorizer.ResolverWithOpts(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.resolver = resolver
|
||||
return resolver, nil
|
||||
}
|
||||
|
||||
// allocate a cache if option is set
|
||||
@@ -104,6 +119,7 @@ func NewClient(options ...ClientOption) (*Client, error) {
|
||||
}
|
||||
if client.registryAuthorizer == nil {
|
||||
client.registryAuthorizer = ®istryauth.Client{
|
||||
Client: client.httpClient,
|
||||
Header: http.Header{
|
||||
"User-Agent": {version.GetUserAgent()},
|
||||
},
|
||||
@@ -166,6 +182,28 @@ func ClientOptCredentialsFile(credentialsFile string) ClientOption {
|
||||
}
|
||||
}
|
||||
|
||||
// ClientOptHTTPClient returns a function that sets the httpClient setting on a client options set
|
||||
func ClientOptHTTPClient(httpClient *http.Client) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.httpClient = httpClient
|
||||
}
|
||||
}
|
||||
|
||||
func ClientOptPlainHTTP() ClientOption {
|
||||
return func(c *Client) {
|
||||
c.plainHTTP = true
|
||||
}
|
||||
}
|
||||
|
||||
// ClientOptResolver returns a function that sets the resolver setting on a client options set
|
||||
func ClientOptResolver(resolver remotes.Resolver) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.resolver = func(ref registry.Reference) (remotes.Resolver, error) {
|
||||
return resolver, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
// LoginOption allows specifying various settings on login
|
||||
LoginOption func(*loginOperation)
|
||||
@@ -174,6 +212,9 @@ type (
|
||||
username string
|
||||
password string
|
||||
insecure bool
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
}
|
||||
)
|
||||
|
||||
@@ -189,6 +230,7 @@ func (c *Client) Login(host string, options ...LoginOption) error {
|
||||
auth.WithLoginUsername(operation.username),
|
||||
auth.WithLoginSecret(operation.password),
|
||||
auth.WithLoginUserAgent(version.GetUserAgent()),
|
||||
auth.WithLoginTLS(operation.certFile, operation.keyFile, operation.caFile),
|
||||
}
|
||||
if operation.insecure {
|
||||
authorizerLoginOpts = append(authorizerLoginOpts, auth.WithLoginInsecure())
|
||||
@@ -215,6 +257,15 @@ func LoginOptInsecure(insecure bool) LoginOption {
|
||||
}
|
||||
}
|
||||
|
||||
// LoginOptTLSClientConfig returns a function that sets the TLS settings on login.
|
||||
func LoginOptTLSClientConfig(certFile, keyFile, caFile string) LoginOption {
|
||||
return func(operation *loginOperation) {
|
||||
operation.certFile = certFile
|
||||
operation.keyFile = keyFile
|
||||
operation.caFile = caFile
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
// LogoutOption allows specifying various settings on logout
|
||||
LogoutOption func(*logoutOperation)
|
||||
@@ -241,21 +292,21 @@ type (
|
||||
|
||||
// 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"`
|
||||
Manifest *DescriptorPullSummary `json:"manifest"`
|
||||
Config *DescriptorPullSummary `json:"config"`
|
||||
Chart *DescriptorPullSummaryWithMeta `json:"chart"`
|
||||
Prov *DescriptorPullSummary `json:"prov"`
|
||||
Ref string `json:"ref"`
|
||||
}
|
||||
|
||||
descriptorPullSummary struct {
|
||||
DescriptorPullSummary struct {
|
||||
Data []byte `json:"-"`
|
||||
Digest string `json:"digest"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
descriptorPullSummaryWithMeta struct {
|
||||
descriptorPullSummary
|
||||
DescriptorPullSummaryWithMeta struct {
|
||||
DescriptorPullSummary
|
||||
Meta *chart.Metadata `json:"meta"`
|
||||
}
|
||||
|
||||
@@ -300,7 +351,11 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
|
||||
}
|
||||
|
||||
var descriptors, layers []ocispec.Descriptor
|
||||
registryStore := content.Registry{Resolver: c.resolver}
|
||||
remotesResolver, err := c.resolver(parsedRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
registryStore := content.Registry{Resolver: remotesResolver}
|
||||
|
||||
manifest, err := oras.Copy(ctx(c.out, c.debug), registryStore, parsedRef.String(), memoryStore, "",
|
||||
oras.WithPullEmptyNameAllowed(),
|
||||
@@ -354,16 +409,16 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
|
||||
}
|
||||
}
|
||||
result := &PullResult{
|
||||
Manifest: &descriptorPullSummary{
|
||||
Manifest: &DescriptorPullSummary{
|
||||
Digest: manifest.Digest.String(),
|
||||
Size: manifest.Size,
|
||||
},
|
||||
Config: &descriptorPullSummary{
|
||||
Config: &DescriptorPullSummary{
|
||||
Digest: configDescriptor.Digest.String(),
|
||||
Size: configDescriptor.Size,
|
||||
},
|
||||
Chart: &descriptorPullSummaryWithMeta{},
|
||||
Prov: &descriptorPullSummary{},
|
||||
Chart: &DescriptorPullSummaryWithMeta{},
|
||||
Prov: &DescriptorPullSummary{},
|
||||
Ref: parsedRef.String(),
|
||||
}
|
||||
var getManifestErr error
|
||||
@@ -474,6 +529,7 @@ type (
|
||||
pushOperation struct {
|
||||
provData []byte
|
||||
strictMode bool
|
||||
test bool
|
||||
}
|
||||
)
|
||||
|
||||
@@ -527,7 +583,9 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu
|
||||
descriptors = append(descriptors, provDescriptor)
|
||||
}
|
||||
|
||||
manifestData, manifest, err := content.GenerateManifest(&configDescriptor, nil, descriptors...)
|
||||
ociAnnotations := generateOCIAnnotations(meta, operation.test)
|
||||
|
||||
manifestData, manifest, err := content.GenerateManifest(&configDescriptor, ociAnnotations, descriptors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -536,7 +594,11 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registryStore := content.Registry{Resolver: c.resolver}
|
||||
remotesResolver, err := c.resolver(parsedRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
registryStore := content.Registry{Resolver: remotesResolver}
|
||||
_, err = oras.Copy(ctx(c.out, c.debug), memoryStore, parsedRef.String(), registryStore, "",
|
||||
oras.WithNameValidation(nil))
|
||||
if err != nil {
|
||||
@@ -590,6 +652,13 @@ func PushOptStrictMode(strictMode bool) PushOption {
|
||||
}
|
||||
}
|
||||
|
||||
// PushOptTest returns a function that sets whether test setting on push
|
||||
func PushOptTest(test bool) PushOption {
|
||||
return func(operation *pushOperation) {
|
||||
operation.test = test
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -600,23 +669,14 @@ func (c *Client) Tags(ref string) ([]string, error) {
|
||||
repository := registryremote.Repository{
|
||||
Reference: parsedReference,
|
||||
Client: c.registryAuthorizer,
|
||||
PlainHTTP: c.plainHTTP,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
registryTags, err = registry.Tags(ctx(c.out, c.debug), &repository)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var tagVersions []*semver.Version
|
||||
|
||||
116
vendor/helm.sh/helm/v3/pkg/registry/util.go
vendored
116
vendor/helm.sh/helm/v3/pkg/registry/util.go
vendored
@@ -21,18 +21,29 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"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/internal/tlsutil"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
)
|
||||
|
||||
var immutableOciAnnotations = []string{
|
||||
ocispec.AnnotationVersion,
|
||||
ocispec.AnnotationTitle,
|
||||
}
|
||||
|
||||
// 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))
|
||||
@@ -129,3 +140,108 @@ func parseReference(raw string) (registry.Reference, error) {
|
||||
|
||||
return registry.ParseReference(raw)
|
||||
}
|
||||
|
||||
// NewRegistryClientWithTLS is a helper function to create a new registry client with TLS enabled.
|
||||
func NewRegistryClientWithTLS(out io.Writer, certFile, keyFile, caFile string, insecureSkipTLSverify bool, registryConfig string, debug bool) (*Client, error) {
|
||||
tlsConf, err := tlsutil.NewClientTLS(certFile, keyFile, caFile, insecureSkipTLSverify)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create TLS config for client: %s", err)
|
||||
}
|
||||
// Create a new registry client
|
||||
registryClient, err := NewClient(
|
||||
ClientOptDebug(debug),
|
||||
ClientOptEnableCache(true),
|
||||
ClientOptWriter(out),
|
||||
ClientOptCredentialsFile(registryConfig),
|
||||
ClientOptHTTPClient(&http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: tlsConf,
|
||||
},
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registryClient, nil
|
||||
}
|
||||
|
||||
// generateOCIAnnotations will generate OCI annotations to include within the OCI manifest
|
||||
func generateOCIAnnotations(meta *chart.Metadata, test bool) map[string]string {
|
||||
|
||||
// Get annotations from Chart attributes
|
||||
ociAnnotations := generateChartOCIAnnotations(meta, test)
|
||||
|
||||
// Copy Chart annotations
|
||||
annotations:
|
||||
for chartAnnotationKey, chartAnnotationValue := range meta.Annotations {
|
||||
|
||||
// Avoid overriding key properties
|
||||
for _, immutableOciKey := range immutableOciAnnotations {
|
||||
if immutableOciKey == chartAnnotationKey {
|
||||
continue annotations
|
||||
}
|
||||
}
|
||||
|
||||
// Add chart annotation
|
||||
ociAnnotations[chartAnnotationKey] = chartAnnotationValue
|
||||
}
|
||||
|
||||
return ociAnnotations
|
||||
}
|
||||
|
||||
// getChartOCIAnnotations will generate OCI annotations from the provided chart
|
||||
func generateChartOCIAnnotations(meta *chart.Metadata, test bool) map[string]string {
|
||||
chartOCIAnnotations := map[string]string{}
|
||||
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationDescription, meta.Description)
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationTitle, meta.Name)
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationVersion, meta.Version)
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationURL, meta.Home)
|
||||
|
||||
if !test {
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationCreated, helmtime.Now().UTC().Format(time.RFC3339))
|
||||
}
|
||||
|
||||
if len(meta.Sources) > 0 {
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationSource, meta.Sources[0])
|
||||
}
|
||||
|
||||
if meta.Maintainers != nil && len(meta.Maintainers) > 0 {
|
||||
var maintainerSb strings.Builder
|
||||
|
||||
for maintainerIdx, maintainer := range meta.Maintainers {
|
||||
|
||||
if len(maintainer.Name) > 0 {
|
||||
maintainerSb.WriteString(maintainer.Name)
|
||||
}
|
||||
|
||||
if len(maintainer.Email) > 0 {
|
||||
maintainerSb.WriteString(" (")
|
||||
maintainerSb.WriteString(maintainer.Email)
|
||||
maintainerSb.WriteString(")")
|
||||
}
|
||||
|
||||
if maintainerIdx < len(meta.Maintainers)-1 {
|
||||
maintainerSb.WriteString(", ")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
chartOCIAnnotations = addToMap(chartOCIAnnotations, ocispec.AnnotationAuthors, maintainerSb.String())
|
||||
|
||||
}
|
||||
|
||||
return chartOCIAnnotations
|
||||
}
|
||||
|
||||
// addToMap takes an existing map and adds an item if the value is not empty
|
||||
func addToMap(inputMap map[string]string, newKey string, newValue string) map[string]string {
|
||||
|
||||
// Add item to map if its
|
||||
if len(strings.TrimSpace(newValue)) > 0 {
|
||||
inputMap[newKey] = newValue
|
||||
}
|
||||
|
||||
return inputMap
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ type KindSortOrder []string
|
||||
//
|
||||
// Those occurring earlier in the list get installed before those occurring later in the list.
|
||||
var InstallOrder KindSortOrder = []string{
|
||||
"PriorityClass",
|
||||
"Namespace",
|
||||
"NetworkPolicy",
|
||||
"ResourceQuota",
|
||||
@@ -105,6 +106,7 @@ var UninstallOrder KindSortOrder = []string{
|
||||
"ResourceQuota",
|
||||
"NetworkPolicy",
|
||||
"Namespace",
|
||||
"PriorityClass",
|
||||
}
|
||||
|
||||
// sort manifests by kind.
|
||||
@@ -130,7 +132,7 @@ func sortHooksByKind(hooks []*release.Hook, ordering KindSortOrder) []*release.H
|
||||
return h
|
||||
}
|
||||
|
||||
func lessByKind(a interface{}, b interface{}, kindA string, kindB string, o KindSortOrder) bool {
|
||||
func lessByKind(_ interface{}, _ interface{}, kindA string, kindB string, o KindSortOrder) bool {
|
||||
ordering := make(map[string]int, len(o))
|
||||
for v, k := range o {
|
||||
ordering[k] = v
|
||||
|
||||
@@ -117,19 +117,19 @@ func SortManifests(files map[string]string, apis chartutil.VersionSet, ordering
|
||||
//
|
||||
// To determine hook type, it looks for a YAML structure like this:
|
||||
//
|
||||
// kind: SomeKind
|
||||
// apiVersion: v1
|
||||
// metadata:
|
||||
// annotations:
|
||||
// helm.sh/hook: pre-install
|
||||
// kind: SomeKind
|
||||
// apiVersion: v1
|
||||
// metadata:
|
||||
// annotations:
|
||||
// helm.sh/hook: pre-install
|
||||
//
|
||||
// To determine the policy to delete the hook, it looks for a YAML structure like this:
|
||||
//
|
||||
// kind: SomeKind
|
||||
// apiVersion: v1
|
||||
// metadata:
|
||||
// annotations:
|
||||
// helm.sh/hook-delete-policy: hook-succeeded
|
||||
// kind: SomeKind
|
||||
// apiVersion: v1
|
||||
// metadata:
|
||||
// annotations:
|
||||
// helm.sh/hook-delete-policy: hook-succeeded
|
||||
func (file *manifestFile) sort(result *result) error {
|
||||
// Go through manifests in order found in file (function `SplitManifests` creates integer-sortable keys)
|
||||
var sortedEntryKeys []string
|
||||
|
||||
10
vendor/helm.sh/helm/v3/pkg/repo/chartrepo.go
vendored
10
vendor/helm.sh/helm/v3/pkg/repo/chartrepo.go
vendored
@@ -21,7 +21,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -131,7 +131,7 @@ func (r *ChartRepository) DownloadIndexFile() (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
index, err := ioutil.ReadAll(resp)
|
||||
index, err := io.ReadAll(resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -148,12 +148,12 @@ func (r *ChartRepository) DownloadIndexFile() (string, error) {
|
||||
}
|
||||
chartsFile := filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name))
|
||||
os.MkdirAll(filepath.Dir(chartsFile), 0755)
|
||||
ioutil.WriteFile(chartsFile, []byte(charts.String()), 0644)
|
||||
os.WriteFile(chartsFile, []byte(charts.String()), 0644)
|
||||
|
||||
// Create the index file in the cache directory
|
||||
fname := filepath.Join(r.CachePath, helmpath.CacheIndexFile(r.Config.Name))
|
||||
os.MkdirAll(filepath.Dir(fname), 0755)
|
||||
return fname, ioutil.WriteFile(fname, index, 0644)
|
||||
return fname, os.WriteFile(fname, index, 0644)
|
||||
}
|
||||
|
||||
// Index generates an index for the chart repository and writes an index.yaml file.
|
||||
@@ -170,7 +170,7 @@ func (r *ChartRepository) saveIndexFile() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(filepath.Join(r.Config.Name, indexPath), index, 0644)
|
||||
return os.WriteFile(filepath.Join(r.Config.Name, indexPath), index, 0644)
|
||||
}
|
||||
|
||||
func (r *ChartRepository) generateIndex() error {
|
||||
|
||||
9
vendor/helm.sh/helm/v3/pkg/repo/doc.go
vendored
9
vendor/helm.sh/helm/v3/pkg/repo/doc.go
vendored
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*Package repo implements the Helm Chart Repository.
|
||||
/*
|
||||
Package repo implements the Helm Chart Repository.
|
||||
|
||||
A chart repository is an HTTP server that provides information on charts. A local
|
||||
repository cache is an on-disk representation of a chart repository.
|
||||
@@ -83,9 +84,9 @@ The format of a repository.yaml file is:
|
||||
|
||||
This file maps three bits of information about a repository:
|
||||
|
||||
- The name the user uses to refer to it
|
||||
- The fully qualified URL to the repository (index.yaml will be appended)
|
||||
- The name of the local cachefile
|
||||
- The name the user uses to refer to it
|
||||
- The fully qualified URL to the repository (index.yaml will be appended)
|
||||
- The name of the local cachefile
|
||||
|
||||
The format for both files was changed after Helm v2.0.0-Alpha.4. Helm is not
|
||||
backwards compatible with those earlier versions.
|
||||
|
||||
36
vendor/helm.sh/helm/v3/pkg/repo/index.go
vendored
36
vendor/helm.sh/helm/v3/pkg/repo/index.go
vendored
@@ -18,7 +18,7 @@ package repo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
@@ -104,7 +104,7 @@ func NewIndexFile() *IndexFile {
|
||||
|
||||
// LoadIndexFile takes a file at the given path and returns an IndexFile object
|
||||
func LoadIndexFile(path string) (*IndexFile, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -233,6 +233,18 @@ func (i IndexFile) WriteFile(dest string, mode os.FileMode) error {
|
||||
return fileutil.AtomicWriteFile(dest, bytes.NewReader(b), mode)
|
||||
}
|
||||
|
||||
// WriteJSONFile writes an index file in JSON format to the given destination
|
||||
// path.
|
||||
//
|
||||
// The mode on the file is set to 'mode'.
|
||||
func (i IndexFile) WriteJSONFile(dest string, mode os.FileMode) error {
|
||||
b, err := json.MarshalIndent(i, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fileutil.AtomicWriteFile(dest, bytes.NewReader(b), mode)
|
||||
}
|
||||
|
||||
// Merge merges the given index file into this index.
|
||||
//
|
||||
// This merges by name and version.
|
||||
@@ -337,7 +349,7 @@ func loadIndex(data []byte, source string) (*IndexFile, error) {
|
||||
return i, ErrEmptyIndexYaml
|
||||
}
|
||||
|
||||
if err := yaml.UnmarshalStrict(data, i); err != nil {
|
||||
if err := jsonOrYamlUnmarshal(data, i); err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -347,6 +359,10 @@ func loadIndex(data []byte, source string) (*IndexFile, error) {
|
||||
log.Printf("skipping loading invalid entry for chart %q from %s: empty entry", name, source)
|
||||
continue
|
||||
}
|
||||
// When metadata section missing, initialize with no data
|
||||
if cvs[idx].Metadata == nil {
|
||||
cvs[idx].Metadata = &chart.Metadata{}
|
||||
}
|
||||
if cvs[idx].APIVersion == "" {
|
||||
cvs[idx].APIVersion = chart.APIVersionV1
|
||||
}
|
||||
@@ -362,3 +378,17 @@ func loadIndex(data []byte, source string) (*IndexFile, error) {
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// jsonOrYamlUnmarshal unmarshals the given byte slice containing JSON or YAML
|
||||
// into the provided interface.
|
||||
//
|
||||
// It automatically detects whether the data is in JSON or YAML format by
|
||||
// checking its validity as JSON. If the data is valid JSON, it will use the
|
||||
// `encoding/json` package to unmarshal it. Otherwise, it will use the
|
||||
// `sigs.k8s.io/yaml` package to unmarshal the YAML data.
|
||||
func jsonOrYamlUnmarshal(b []byte, i interface{}) error {
|
||||
if json.Valid(b) {
|
||||
return json.Unmarshal(b, i)
|
||||
}
|
||||
return yaml.UnmarshalStrict(b, i)
|
||||
}
|
||||
|
||||
5
vendor/helm.sh/helm/v3/pkg/repo/repo.go
vendored
5
vendor/helm.sh/helm/v3/pkg/repo/repo.go
vendored
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package repo // import "helm.sh/helm/v3/pkg/repo"
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
@@ -47,7 +46,7 @@ func NewFile() *File {
|
||||
// LoadFile takes a file at the given path and returns a File object
|
||||
func LoadFile(path string) (*File, error) {
|
||||
r := new(File)
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return r, errors.Wrapf(err, "couldn't load repositories file (%s)", path)
|
||||
}
|
||||
@@ -122,5 +121,5 @@ func (r *File) WriteFile(path string, perm os.FileMode) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(path, data, perm)
|
||||
return os.WriteFile(path, data, perm)
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) {
|
||||
cfgmaps.Log("get: failed to decode data %q: %s", key, err)
|
||||
return nil, err
|
||||
}
|
||||
r.Labels = filterSystemLabels(obj.ObjectMeta.Labels)
|
||||
// return the release object
|
||||
return r, nil
|
||||
}
|
||||
@@ -145,6 +146,7 @@ func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, err
|
||||
cfgmaps.Log("query: failed to decode release: %s", err)
|
||||
continue
|
||||
}
|
||||
rls.Labels = item.ObjectMeta.Labels
|
||||
results = append(results, rls)
|
||||
}
|
||||
return results, nil
|
||||
@@ -157,6 +159,7 @@ func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error {
|
||||
var lbs labels
|
||||
|
||||
lbs.init()
|
||||
lbs.fromMap(rls.Labels)
|
||||
lbs.set("createdAt", strconv.Itoa(int(time.Now().Unix())))
|
||||
|
||||
// create a new configmap to hold the release
|
||||
@@ -184,6 +187,7 @@ func (cfgmaps *ConfigMaps) Update(key string, rls *rspb.Release) error {
|
||||
var lbs labels
|
||||
|
||||
lbs.init()
|
||||
lbs.fromMap(rls.Labels)
|
||||
lbs.set("modifiedAt", strconv.Itoa(int(time.Now().Unix())))
|
||||
|
||||
// create a new configmap object to hold the release
|
||||
@@ -220,13 +224,12 @@ func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) {
|
||||
//
|
||||
// The following labels are used within each configmap:
|
||||
//
|
||||
// "modifiedAt" - timestamp indicating when this configmap was last modified. (set in Update)
|
||||
// "createdAt" - timestamp indicating when this configmap was created. (set in Create)
|
||||
// "version" - version of the release.
|
||||
// "status" - status of the release (see pkg/release/status.go for variants)
|
||||
// "owner" - owner of the configmap, currently "helm".
|
||||
// "name" - name of the release.
|
||||
//
|
||||
// "modifiedAt" - timestamp indicating when this configmap was last modified. (set in Update)
|
||||
// "createdAt" - timestamp indicating when this configmap was created. (set in Create)
|
||||
// "version" - version of the release.
|
||||
// "status" - status of the release (see pkg/release/status.go for variants)
|
||||
// "owner" - owner of the configmap, currently "helm".
|
||||
// "name" - name of the release.
|
||||
func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*v1.ConfigMap, error) {
|
||||
const owner = "helm"
|
||||
|
||||
@@ -240,6 +243,9 @@ func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*v1.ConfigM
|
||||
lbs.init()
|
||||
}
|
||||
|
||||
// apply custom labels
|
||||
lbs.fromMap(rls.Labels)
|
||||
|
||||
// apply labels
|
||||
lbs.set("name", rls.Name)
|
||||
lbs.set("owner", owner)
|
||||
|
||||
@@ -72,6 +72,7 @@ func (secrets *Secrets) Get(key string) (*rspb.Release, error) {
|
||||
}
|
||||
// found the secret, decode the base64 data string
|
||||
r, err := decodeRelease(string(obj.Data["release"]))
|
||||
r.Labels = filterSystemLabels(obj.ObjectMeta.Labels)
|
||||
return r, errors.Wrapf(err, "get: failed to decode data %q", key)
|
||||
}
|
||||
|
||||
@@ -136,6 +137,7 @@ func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error)
|
||||
secrets.Log("query: failed to decode release: %s", err)
|
||||
continue
|
||||
}
|
||||
rls.Labels = item.ObjectMeta.Labels
|
||||
results = append(results, rls)
|
||||
}
|
||||
return results, nil
|
||||
@@ -148,6 +150,7 @@ func (secrets *Secrets) Create(key string, rls *rspb.Release) error {
|
||||
var lbs labels
|
||||
|
||||
lbs.init()
|
||||
lbs.fromMap(rls.Labels)
|
||||
lbs.set("createdAt", strconv.Itoa(int(time.Now().Unix())))
|
||||
|
||||
// create a new secret to hold the release
|
||||
@@ -173,6 +176,7 @@ func (secrets *Secrets) Update(key string, rls *rspb.Release) error {
|
||||
var lbs labels
|
||||
|
||||
lbs.init()
|
||||
lbs.fromMap(rls.Labels)
|
||||
lbs.set("modifiedAt", strconv.Itoa(int(time.Now().Unix())))
|
||||
|
||||
// create a new secret object to hold the release
|
||||
@@ -202,13 +206,12 @@ func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) {
|
||||
//
|
||||
// The following labels are used within each secret:
|
||||
//
|
||||
// "modifiedAt" - timestamp indicating when this secret was last modified. (set in Update)
|
||||
// "createdAt" - timestamp indicating when this secret was created. (set in Create)
|
||||
// "version" - version of the release.
|
||||
// "status" - status of the release (see pkg/release/status.go for variants)
|
||||
// "owner" - owner of the secret, currently "helm".
|
||||
// "name" - name of the release.
|
||||
//
|
||||
// "modifiedAt" - timestamp indicating when this secret was last modified. (set in Update)
|
||||
// "createdAt" - timestamp indicating when this secret was created. (set in Create)
|
||||
// "version" - version of the release.
|
||||
// "status" - status of the release (see pkg/release/status.go for variants)
|
||||
// "owner" - owner of the secret, currently "helm".
|
||||
// "name" - name of the release.
|
||||
func newSecretsObject(key string, rls *rspb.Release, lbs labels) (*v1.Secret, error) {
|
||||
const owner = "helm"
|
||||
|
||||
@@ -222,6 +225,9 @@ func newSecretsObject(key string, rls *rspb.Release, lbs labels) (*v1.Secret, er
|
||||
lbs.init()
|
||||
}
|
||||
|
||||
// apply custom labels
|
||||
lbs.fromMap(rls.Labels)
|
||||
|
||||
// apply labels
|
||||
lbs.set("name", rls.Name)
|
||||
lbs.set("owner", owner)
|
||||
|
||||
215
vendor/helm.sh/helm/v3/pkg/storage/driver/sql.go
vendored
215
vendor/helm.sh/helm/v3/pkg/storage/driver/sql.go
vendored
@@ -19,6 +19,7 @@ package driver // import "helm.sh/helm/v3/pkg/storage/driver"
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
@@ -49,6 +50,7 @@ const postgreSQLDialect = "postgres"
|
||||
const SQLDriverName = "SQL"
|
||||
|
||||
const sqlReleaseTableName = "releases_v1"
|
||||
const sqlCustomLabelsTableName = "custom_labels_v1"
|
||||
|
||||
const (
|
||||
sqlReleaseTableKeyColumn = "key"
|
||||
@@ -61,6 +63,17 @@ const (
|
||||
sqlReleaseTableOwnerColumn = "owner"
|
||||
sqlReleaseTableCreatedAtColumn = "createdAt"
|
||||
sqlReleaseTableModifiedAtColumn = "modifiedAt"
|
||||
|
||||
sqlCustomLabelsTableReleaseKeyColumn = "releaseKey"
|
||||
sqlCustomLabelsTableReleaseNamespaceColumn = "releaseNamespace"
|
||||
sqlCustomLabelsTableKeyColumn = "key"
|
||||
sqlCustomLabelsTableValueColumn = "value"
|
||||
)
|
||||
|
||||
// Following limits based on k8s labels limits - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set
|
||||
const (
|
||||
sqlCustomLabelsTableKeyMaxLenght = 253 + 1 + 63
|
||||
sqlCustomLabelsTableValueMaxLenght = 63
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -82,8 +95,42 @@ func (s *SQL) Name() string {
|
||||
return SQLDriverName
|
||||
}
|
||||
|
||||
// Check if all migrations al
|
||||
func (s *SQL) checkAlreadyApplied(migrations []*migrate.Migration) bool {
|
||||
// make map (set) of ids for fast search
|
||||
migrationsIds := make(map[string]struct{})
|
||||
for _, migration := range migrations {
|
||||
migrationsIds[migration.Id] = struct{}{}
|
||||
}
|
||||
|
||||
// get list of applied migrations
|
||||
migrate.SetDisableCreateTable(true)
|
||||
records, err := migrate.GetMigrationRecords(s.db.DB, postgreSQLDialect)
|
||||
migrate.SetDisableCreateTable(false)
|
||||
if err != nil {
|
||||
s.Log("checkAlreadyApplied: failed to get migration records: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
if _, ok := migrationsIds[record.Id]; ok {
|
||||
s.Log("checkAlreadyApplied: found previous migration (Id: %v) applied at %v", record.Id, record.AppliedAt)
|
||||
delete(migrationsIds, record.Id)
|
||||
}
|
||||
}
|
||||
|
||||
// check if all migrations appliyed
|
||||
if len(migrationsIds) != 0 {
|
||||
for id := range migrationsIds {
|
||||
s.Log("checkAlreadyApplied: find unapplied migration (id: %v)", id)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *SQL) ensureDBSetup() error {
|
||||
// Populate the database with the relations we need if they don't exist yet
|
||||
|
||||
migrations := &migrate.MemoryMigrationSource{
|
||||
Migrations: []*migrate.Migration{
|
||||
{
|
||||
@@ -91,7 +138,7 @@ func (s *SQL) ensureDBSetup() error {
|
||||
Up: []string{
|
||||
fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
%s VARCHAR(67),
|
||||
%s VARCHAR(90),
|
||||
%s VARCHAR(64) NOT NULL,
|
||||
%s TEXT NOT NULL,
|
||||
%s VARCHAR(64) NOT NULL,
|
||||
@@ -109,9 +156,9 @@ func (s *SQL) ensureDBSetup() error {
|
||||
CREATE INDEX ON %s (%s);
|
||||
CREATE INDEX ON %s (%s);
|
||||
CREATE INDEX ON %s (%s);
|
||||
|
||||
|
||||
GRANT ALL ON %s TO PUBLIC;
|
||||
|
||||
|
||||
ALTER TABLE %s ENABLE ROW LEVEL SECURITY;
|
||||
`,
|
||||
sqlReleaseTableName,
|
||||
@@ -150,9 +197,50 @@ func (s *SQL) ensureDBSetup() error {
|
||||
`, sqlReleaseTableName),
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "custom_labels",
|
||||
Up: []string{
|
||||
fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
%s VARCHAR(64),
|
||||
%s VARCHAR(67),
|
||||
%s VARCHAR(%d),
|
||||
%s VARCHAR(%d)
|
||||
);
|
||||
CREATE INDEX ON %s (%s, %s);
|
||||
|
||||
GRANT ALL ON %s TO PUBLIC;
|
||||
ALTER TABLE %s ENABLE ROW LEVEL SECURITY;
|
||||
`,
|
||||
sqlCustomLabelsTableName,
|
||||
sqlCustomLabelsTableReleaseKeyColumn,
|
||||
sqlCustomLabelsTableReleaseNamespaceColumn,
|
||||
sqlCustomLabelsTableKeyColumn,
|
||||
sqlCustomLabelsTableKeyMaxLenght,
|
||||
sqlCustomLabelsTableValueColumn,
|
||||
sqlCustomLabelsTableValueMaxLenght,
|
||||
sqlCustomLabelsTableName,
|
||||
sqlCustomLabelsTableReleaseKeyColumn,
|
||||
sqlCustomLabelsTableReleaseNamespaceColumn,
|
||||
sqlCustomLabelsTableName,
|
||||
sqlCustomLabelsTableName,
|
||||
),
|
||||
},
|
||||
Down: []string{
|
||||
fmt.Sprintf(`
|
||||
DELETE TABLE %s;
|
||||
`, sqlCustomLabelsTableName),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Check that init migration already applied
|
||||
if s.checkAlreadyApplied(migrations.Migrations) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Populate the database with the relations we need if they don't exist yet
|
||||
_, err := migrate.Exec(s.db.DB, postgreSQLDialect, migrations, migrate.Up)
|
||||
return err
|
||||
}
|
||||
@@ -180,6 +268,13 @@ type SQLReleaseWrapper struct {
|
||||
ModifiedAt int `db:"modifiedAt"`
|
||||
}
|
||||
|
||||
type SQLReleaseCustomLabelWrapper struct {
|
||||
ReleaseKey string `db:"release_key"`
|
||||
ReleaseNamespace string `db:"release_namespace"`
|
||||
Key string `db:"key"`
|
||||
Value string `db:"value"`
|
||||
}
|
||||
|
||||
// NewSQL initializes a new sql driver.
|
||||
func NewSQL(connectionString string, logger func(string, ...interface{}), namespace string) (*SQL, error) {
|
||||
db, err := sqlx.Connect(postgreSQLDialect, connectionString)
|
||||
@@ -230,13 +325,18 @@ func (s *SQL) Get(key string) (*rspb.Release, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if release.Labels, err = s.getReleaseCustomLabels(key, s.namespace); err != nil {
|
||||
s.Log("failed to get release %s/%s custom labels: %v", s.namespace, key, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return release, nil
|
||||
}
|
||||
|
||||
// List returns the list of all releases such that filter(release) == true
|
||||
func (s *SQL) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
|
||||
sb := s.statementBuilder.
|
||||
Select(sqlReleaseTableBodyColumn).
|
||||
Select(sqlReleaseTableKeyColumn, sqlReleaseTableNamespaceColumn, sqlReleaseTableBodyColumn).
|
||||
From(sqlReleaseTableName).
|
||||
Where(sq.Eq{sqlReleaseTableOwnerColumn: sqlReleaseDefaultOwner})
|
||||
|
||||
@@ -264,6 +364,15 @@ func (s *SQL) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
|
||||
s.Log("list: failed to decode release: %v: %v", record, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if release.Labels, err = s.getReleaseCustomLabels(record.Key, record.Namespace); err != nil {
|
||||
s.Log("failed to get release %s/%s custom labels: %v", record.Namespace, record.Key, err)
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range getReleaseSystemLabels(release) {
|
||||
release.Labels[k] = v
|
||||
}
|
||||
|
||||
if filter(release) {
|
||||
releases = append(releases, release)
|
||||
}
|
||||
@@ -275,7 +384,7 @@ func (s *SQL) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
|
||||
// Query returns the set of releases that match the provided set of labels.
|
||||
func (s *SQL) Query(labels map[string]string) ([]*rspb.Release, error) {
|
||||
sb := s.statementBuilder.
|
||||
Select(sqlReleaseTableBodyColumn).
|
||||
Select(sqlReleaseTableKeyColumn, sqlReleaseTableNamespaceColumn, sqlReleaseTableBodyColumn).
|
||||
From(sqlReleaseTableName)
|
||||
|
||||
keys := make([]string, 0, len(labels))
|
||||
@@ -321,6 +430,12 @@ func (s *SQL) Query(labels map[string]string) ([]*rspb.Release, error) {
|
||||
s.Log("list: failed to decode release: %v: %v", record, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if release.Labels, err = s.getReleaseCustomLabels(record.Key, record.Namespace); err != nil {
|
||||
s.Log("failed to get release %s/%s custom labels: %v", record.Namespace, record.Key, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
releases = append(releases, release)
|
||||
}
|
||||
|
||||
@@ -403,6 +518,36 @@ func (s *SQL) Create(key string, rls *rspb.Release) error {
|
||||
s.Log("failed to store release %s in SQL database: %v", key, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Filtering labels before insert cause in SQL storage driver system releases are stored in separate columns of release table
|
||||
for k, v := range filterSystemLabels(rls.Labels) {
|
||||
insertLabelsQuery, args, err := s.statementBuilder.
|
||||
Insert(sqlCustomLabelsTableName).
|
||||
Columns(
|
||||
sqlCustomLabelsTableReleaseKeyColumn,
|
||||
sqlCustomLabelsTableReleaseNamespaceColumn,
|
||||
sqlCustomLabelsTableKeyColumn,
|
||||
sqlCustomLabelsTableValueColumn,
|
||||
).
|
||||
Values(
|
||||
key,
|
||||
namespace,
|
||||
k,
|
||||
v,
|
||||
).ToSql()
|
||||
|
||||
if err != nil {
|
||||
defer transaction.Rollback()
|
||||
s.Log("failed to build insert query: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := transaction.Exec(insertLabelsQuery, args...); err != nil {
|
||||
defer transaction.Rollback()
|
||||
s.Log("failed to write Labels: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer transaction.Commit()
|
||||
|
||||
return nil
|
||||
@@ -487,10 +632,66 @@ func (s *SQL) Delete(key string) (*rspb.Release, error) {
|
||||
Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
s.Log("failed to build select query: %v", err)
|
||||
s.Log("failed to build delete query: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = transaction.Exec(deleteQuery, args...)
|
||||
if err != nil {
|
||||
s.Log("failed perform delete query: %v", err)
|
||||
return release, err
|
||||
}
|
||||
|
||||
if release.Labels, err = s.getReleaseCustomLabels(key, s.namespace); err != nil {
|
||||
s.Log("failed to get release %s/%s custom labels: %v", s.namespace, key, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deleteCustomLabelsQuery, args, err := s.statementBuilder.
|
||||
Delete(sqlCustomLabelsTableName).
|
||||
Where(sq.Eq{sqlCustomLabelsTableReleaseKeyColumn: key}).
|
||||
Where(sq.Eq{sqlCustomLabelsTableReleaseNamespaceColumn: s.namespace}).
|
||||
ToSql()
|
||||
|
||||
if err != nil {
|
||||
s.Log("failed to build delete Labels query: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
_, err = transaction.Exec(deleteCustomLabelsQuery, args...)
|
||||
return release, err
|
||||
}
|
||||
|
||||
// Get release custom labels from database
|
||||
func (s *SQL) getReleaseCustomLabels(key string, _ string) (map[string]string, error) {
|
||||
query, args, err := s.statementBuilder.
|
||||
Select(sqlCustomLabelsTableKeyColumn, sqlCustomLabelsTableValueColumn).
|
||||
From(sqlCustomLabelsTableName).
|
||||
Where(sq.Eq{sqlCustomLabelsTableReleaseKeyColumn: key,
|
||||
sqlCustomLabelsTableReleaseNamespaceColumn: s.namespace}).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var labelsList = []SQLReleaseCustomLabelWrapper{}
|
||||
if err := s.db.Select(&labelsList, query, args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labelsMap := make(map[string]string)
|
||||
for _, i := range labelsList {
|
||||
labelsMap[i.Key] = i.Value
|
||||
}
|
||||
|
||||
return filterSystemLabels(labelsMap), nil
|
||||
}
|
||||
|
||||
// Rebuild system labels from release object
|
||||
func getReleaseSystemLabels(rls *rspb.Release) map[string]string {
|
||||
return map[string]string{
|
||||
"name": rls.Name,
|
||||
"owner": sqlReleaseDefaultOwner,
|
||||
"status": rls.Info.Status.String(),
|
||||
"version": strconv.Itoa(rls.Version),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
|
||||
rspb "helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
@@ -30,6 +30,8 @@ var b64 = base64.StdEncoding
|
||||
|
||||
var magicGzip = []byte{0x1f, 0x8b, 0x08}
|
||||
|
||||
var systemLabels = []string{"name", "owner", "status", "version", "createdAt", "modifiedAt"}
|
||||
|
||||
// encodeRelease encodes a release returning a base64 encoded
|
||||
// gzipped string representation, or error.
|
||||
func encodeRelease(rls *rspb.Release) (string, error) {
|
||||
@@ -69,7 +71,7 @@ func decodeRelease(data string) (*rspb.Release, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
b2, err := ioutil.ReadAll(r)
|
||||
b2, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -83,3 +85,38 @@ func decodeRelease(data string) (*rspb.Release, error) {
|
||||
}
|
||||
return &rls, nil
|
||||
}
|
||||
|
||||
// Checks if label is system
|
||||
func isSystemLabel(key string) bool {
|
||||
for _, v := range GetSystemLabels() {
|
||||
if key == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Removes system labels from labels map
|
||||
func filterSystemLabels(lbs map[string]string) map[string]string {
|
||||
result := make(map[string]string)
|
||||
for k, v := range lbs {
|
||||
if !isSystemLabel(k) {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Checks if labels array contains system labels
|
||||
func ContainsSystemLabels(lbs map[string]string) bool {
|
||||
for k := range lbs {
|
||||
if isSystemLabel(k) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetSystemLabels() []string {
|
||||
return systemLabels
|
||||
}
|
||||
|
||||
32
vendor/helm.sh/helm/v3/pkg/strvals/doc.go
vendored
32
vendor/helm.sh/helm/v3/pkg/strvals/doc.go
vendored
@@ -1,32 +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 strvals provides tools for working with strval lines.
|
||||
|
||||
Helm supports a compressed format for YAML settings which we call strvals.
|
||||
The format is roughly like this:
|
||||
|
||||
name=value,topname.subname=value
|
||||
|
||||
The above is equivalent to the YAML document
|
||||
|
||||
name: value
|
||||
topname:
|
||||
subname: value
|
||||
|
||||
This package provides a parser and utilities for converting the strvals format
|
||||
to other formats.
|
||||
*/
|
||||
package strvals
|
||||
561
vendor/helm.sh/helm/v3/pkg/strvals/parser.go
vendored
561
vendor/helm.sh/helm/v3/pkg/strvals/parser.go
vendored
@@ -1,561 +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 strvals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// ErrNotList indicates that a non-list was treated as a list.
|
||||
var ErrNotList = errors.New("not a list")
|
||||
|
||||
// MaxIndex is the maximum index that will be allowed by setIndex.
|
||||
// The default value 65536 = 1024 * 64
|
||||
var MaxIndex = 65536
|
||||
|
||||
// MaxNestedNameLevel is the maximum level of nesting for a value name that
|
||||
// will be allowed.
|
||||
var MaxNestedNameLevel = 30
|
||||
|
||||
// ToYAML takes a string of arguments and converts to a YAML document.
|
||||
func ToYAML(s string) (string, error) {
|
||||
m, err := Parse(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
d, err := yaml.Marshal(m)
|
||||
return strings.TrimSuffix(string(d), "\n"), err
|
||||
}
|
||||
|
||||
// Parse parses a set line.
|
||||
//
|
||||
// A set line is of the form name1=value1,name2=value2
|
||||
func Parse(s string) (map[string]interface{}, error) {
|
||||
vals := map[string]interface{}{}
|
||||
scanner := bytes.NewBufferString(s)
|
||||
t := newParser(scanner, vals, false)
|
||||
err := t.parse()
|
||||
return vals, err
|
||||
}
|
||||
|
||||
// ParseString parses a set line and forces a string value.
|
||||
//
|
||||
// A set line is of the form name1=value1,name2=value2
|
||||
func ParseString(s string) (map[string]interface{}, error) {
|
||||
vals := map[string]interface{}{}
|
||||
scanner := bytes.NewBufferString(s)
|
||||
t := newParser(scanner, vals, true)
|
||||
err := t.parse()
|
||||
return vals, err
|
||||
}
|
||||
|
||||
// ParseInto parses a strvals line and merges the result into dest.
|
||||
//
|
||||
// If the strval string has a key that exists in dest, it overwrites the
|
||||
// dest version.
|
||||
func ParseInto(s string, dest map[string]interface{}) error {
|
||||
scanner := bytes.NewBufferString(s)
|
||||
t := newParser(scanner, dest, false)
|
||||
return t.parse()
|
||||
}
|
||||
|
||||
// ParseFile parses a set line, but its final value is loaded from the file at the path specified by the original value.
|
||||
//
|
||||
// A set line is of the form name1=path1,name2=path2
|
||||
//
|
||||
// When the files at path1 and path2 contained "val1" and "val2" respectively, the set line is consumed as
|
||||
// name1=val1,name2=val2
|
||||
func ParseFile(s string, reader RunesValueReader) (map[string]interface{}, error) {
|
||||
vals := map[string]interface{}{}
|
||||
scanner := bytes.NewBufferString(s)
|
||||
t := newFileParser(scanner, vals, reader)
|
||||
err := t.parse()
|
||||
return vals, err
|
||||
}
|
||||
|
||||
// ParseIntoString parses a strvals line and merges the result into dest.
|
||||
//
|
||||
// This method always returns a string as the value.
|
||||
func ParseIntoString(s string, dest map[string]interface{}) error {
|
||||
scanner := bytes.NewBufferString(s)
|
||||
t := newParser(scanner, dest, true)
|
||||
return t.parse()
|
||||
}
|
||||
|
||||
// ParseJSON parses a string with format key1=val1, key2=val2, ...
|
||||
// where values are json strings (null, or scalars, or arrays, or objects).
|
||||
// An empty val is treated as null.
|
||||
//
|
||||
// If a key exists in dest, the new value overwrites the dest version.
|
||||
//
|
||||
func ParseJSON(s string, dest map[string]interface{}) error {
|
||||
scanner := bytes.NewBufferString(s)
|
||||
t := newJSONParser(scanner, dest)
|
||||
return t.parse()
|
||||
}
|
||||
|
||||
// ParseIntoFile parses a filevals line and merges the result into dest.
|
||||
//
|
||||
// This method always returns a string as the value.
|
||||
func ParseIntoFile(s string, dest map[string]interface{}, reader RunesValueReader) error {
|
||||
scanner := bytes.NewBufferString(s)
|
||||
t := newFileParser(scanner, dest, reader)
|
||||
return t.parse()
|
||||
}
|
||||
|
||||
// RunesValueReader is a function that takes the given value (a slice of runes)
|
||||
// and returns the parsed value
|
||||
type RunesValueReader func([]rune) (interface{}, error)
|
||||
|
||||
// parser is a simple parser that takes a strvals line and parses it into a
|
||||
// map representation.
|
||||
//
|
||||
// where sc is the source of the original data being parsed
|
||||
// where data is the final parsed data from the parses with correct types
|
||||
type parser struct {
|
||||
sc *bytes.Buffer
|
||||
data map[string]interface{}
|
||||
reader RunesValueReader
|
||||
isjsonval bool
|
||||
}
|
||||
|
||||
func newParser(sc *bytes.Buffer, data map[string]interface{}, stringBool bool) *parser {
|
||||
stringConverter := func(rs []rune) (interface{}, error) {
|
||||
return typedVal(rs, stringBool), nil
|
||||
}
|
||||
return &parser{sc: sc, data: data, reader: stringConverter}
|
||||
}
|
||||
|
||||
func newJSONParser(sc *bytes.Buffer, data map[string]interface{}) *parser {
|
||||
return &parser{sc: sc, data: data, reader: nil, isjsonval: true}
|
||||
}
|
||||
|
||||
func newFileParser(sc *bytes.Buffer, data map[string]interface{}, reader RunesValueReader) *parser {
|
||||
return &parser{sc: sc, data: data, reader: reader}
|
||||
}
|
||||
|
||||
func (t *parser) parse() error {
|
||||
for {
|
||||
err := t.key(t.data, 0)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func runeSet(r []rune) map[rune]bool {
|
||||
s := make(map[rune]bool, len(r))
|
||||
for _, rr := range r {
|
||||
s[rr] = true
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (t *parser) key(data map[string]interface{}, nestedNameLevel int) (reterr error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
reterr = fmt.Errorf("unable to parse key: %s", r)
|
||||
}
|
||||
}()
|
||||
stop := runeSet([]rune{'=', '[', ',', '.'})
|
||||
for {
|
||||
switch k, last, err := runesUntil(t.sc, stop); {
|
||||
case err != nil:
|
||||
if len(k) == 0 {
|
||||
return err
|
||||
}
|
||||
return errors.Errorf("key %q has no value", string(k))
|
||||
//set(data, string(k), "")
|
||||
//return err
|
||||
case last == '[':
|
||||
// We are in a list index context, so we need to set an index.
|
||||
i, err := t.keyIndex()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error parsing index")
|
||||
}
|
||||
kk := string(k)
|
||||
// Find or create target list
|
||||
list := []interface{}{}
|
||||
if _, ok := data[kk]; ok {
|
||||
list = data[kk].([]interface{})
|
||||
}
|
||||
|
||||
// Now we need to get the value after the ].
|
||||
list, err = t.listItem(list, i, nestedNameLevel)
|
||||
set(data, kk, list)
|
||||
return err
|
||||
case last == '=':
|
||||
if t.isjsonval {
|
||||
empval, err := t.emptyVal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if empval {
|
||||
set(data, string(k), nil)
|
||||
return nil
|
||||
}
|
||||
// parse jsonvals by using Go’s JSON standard library
|
||||
// Decode is preferred to Unmarshal in order to parse just the json parts of the list key1=jsonval1,key2=jsonval2,...
|
||||
// Since Decode has its own buffer that consumes more characters (from underlying t.sc) than the ones actually decoded,
|
||||
// we invoke Decode on a separate reader built with a copy of what is left in t.sc. After Decode is executed, we
|
||||
// discard in t.sc the chars of the decoded json value (the number of those characters is returned by InputOffset).
|
||||
var jsonval interface{}
|
||||
dec := json.NewDecoder(strings.NewReader(t.sc.String()))
|
||||
if err = dec.Decode(&jsonval); err != nil {
|
||||
return err
|
||||
}
|
||||
set(data, string(k), jsonval)
|
||||
if _, err = io.CopyN(ioutil.Discard, t.sc, dec.InputOffset()); err != nil {
|
||||
return err
|
||||
}
|
||||
// skip possible blanks and comma
|
||||
_, err = t.emptyVal()
|
||||
return err
|
||||
}
|
||||
//End of key. Consume =, Get value.
|
||||
// FIXME: Get value list first
|
||||
vl, e := t.valList()
|
||||
switch e {
|
||||
case nil:
|
||||
set(data, string(k), vl)
|
||||
return nil
|
||||
case io.EOF:
|
||||
set(data, string(k), "")
|
||||
return e
|
||||
case ErrNotList:
|
||||
rs, e := t.val()
|
||||
if e != nil && e != io.EOF {
|
||||
return e
|
||||
}
|
||||
v, e := t.reader(rs)
|
||||
set(data, string(k), v)
|
||||
return e
|
||||
default:
|
||||
return e
|
||||
}
|
||||
case last == ',':
|
||||
// No value given. Set the value to empty string. Return error.
|
||||
set(data, string(k), "")
|
||||
return errors.Errorf("key %q has no value (cannot end with ,)", string(k))
|
||||
case last == '.':
|
||||
// Check value name is within the maximum nested name level
|
||||
nestedNameLevel++
|
||||
if nestedNameLevel > MaxNestedNameLevel {
|
||||
return fmt.Errorf("value name nested level is greater than maximum supported nested level of %d", MaxNestedNameLevel)
|
||||
}
|
||||
|
||||
// First, create or find the target map.
|
||||
inner := map[string]interface{}{}
|
||||
if _, ok := data[string(k)]; ok {
|
||||
inner = data[string(k)].(map[string]interface{})
|
||||
}
|
||||
|
||||
// Recurse
|
||||
e := t.key(inner, nestedNameLevel)
|
||||
if e == nil && len(inner) == 0 {
|
||||
return errors.Errorf("key map %q has no value", string(k))
|
||||
}
|
||||
if len(inner) != 0 {
|
||||
set(data, string(k), inner)
|
||||
}
|
||||
return e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func set(data map[string]interface{}, key string, val interface{}) {
|
||||
// If key is empty, don't set it.
|
||||
if len(key) == 0 {
|
||||
return
|
||||
}
|
||||
data[key] = val
|
||||
}
|
||||
|
||||
func setIndex(list []interface{}, index int, val interface{}) (l2 []interface{}, err error) {
|
||||
// There are possible index values that are out of range on a target system
|
||||
// causing a panic. This will catch the panic and return an error instead.
|
||||
// The value of the index that causes a panic varies from system to system.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("error processing index %d: %s", index, r)
|
||||
}
|
||||
}()
|
||||
|
||||
if index < 0 {
|
||||
return list, fmt.Errorf("negative %d index not allowed", index)
|
||||
}
|
||||
if index > MaxIndex {
|
||||
return list, fmt.Errorf("index of %d is greater than maximum supported index of %d", index, MaxIndex)
|
||||
}
|
||||
if len(list) <= index {
|
||||
newlist := make([]interface{}, index+1)
|
||||
copy(newlist, list)
|
||||
list = newlist
|
||||
}
|
||||
list[index] = val
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (t *parser) keyIndex() (int, error) {
|
||||
// First, get the key.
|
||||
stop := runeSet([]rune{']'})
|
||||
v, _, err := runesUntil(t.sc, stop)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// v should be the index
|
||||
return strconv.Atoi(string(v))
|
||||
|
||||
}
|
||||
func (t *parser) listItem(list []interface{}, i, nestedNameLevel int) ([]interface{}, error) {
|
||||
if i < 0 {
|
||||
return list, fmt.Errorf("negative %d index not allowed", i)
|
||||
}
|
||||
stop := runeSet([]rune{'[', '.', '='})
|
||||
switch k, last, err := runesUntil(t.sc, stop); {
|
||||
case len(k) > 0:
|
||||
return list, errors.Errorf("unexpected data at end of array index: %q", k)
|
||||
case err != nil:
|
||||
return list, err
|
||||
case last == '=':
|
||||
if t.isjsonval {
|
||||
empval, err := t.emptyVal()
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
if empval {
|
||||
return setIndex(list, i, nil)
|
||||
}
|
||||
// parse jsonvals by using Go’s JSON standard library
|
||||
// Decode is preferred to Unmarshal in order to parse just the json parts of the list key1=jsonval1,key2=jsonval2,...
|
||||
// Since Decode has its own buffer that consumes more characters (from underlying t.sc) than the ones actually decoded,
|
||||
// we invoke Decode on a separate reader built with a copy of what is left in t.sc. After Decode is executed, we
|
||||
// discard in t.sc the chars of the decoded json value (the number of those characters is returned by InputOffset).
|
||||
var jsonval interface{}
|
||||
dec := json.NewDecoder(strings.NewReader(t.sc.String()))
|
||||
if err = dec.Decode(&jsonval); err != nil {
|
||||
return list, err
|
||||
}
|
||||
if list, err = setIndex(list, i, jsonval); err != nil {
|
||||
return list, err
|
||||
}
|
||||
if _, err = io.CopyN(ioutil.Discard, t.sc, dec.InputOffset()); err != nil {
|
||||
return list, err
|
||||
}
|
||||
// skip possible blanks and comma
|
||||
_, err = t.emptyVal()
|
||||
return list, err
|
||||
}
|
||||
vl, e := t.valList()
|
||||
switch e {
|
||||
case nil:
|
||||
return setIndex(list, i, vl)
|
||||
case io.EOF:
|
||||
return setIndex(list, i, "")
|
||||
case ErrNotList:
|
||||
rs, e := t.val()
|
||||
if e != nil && e != io.EOF {
|
||||
return list, e
|
||||
}
|
||||
v, e := t.reader(rs)
|
||||
if e != nil {
|
||||
return list, e
|
||||
}
|
||||
return setIndex(list, i, v)
|
||||
default:
|
||||
return list, e
|
||||
}
|
||||
case last == '[':
|
||||
// now we have a nested list. Read the index and handle.
|
||||
nextI, err := t.keyIndex()
|
||||
if err != nil {
|
||||
return list, errors.Wrap(err, "error parsing index")
|
||||
}
|
||||
var crtList []interface{}
|
||||
if len(list) > i {
|
||||
// If nested list already exists, take the value of list to next cycle.
|
||||
existed := list[i]
|
||||
if existed != nil {
|
||||
crtList = list[i].([]interface{})
|
||||
}
|
||||
}
|
||||
// Now we need to get the value after the ].
|
||||
list2, err := t.listItem(crtList, nextI, nestedNameLevel)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
return setIndex(list, i, list2)
|
||||
case last == '.':
|
||||
// We have a nested object. Send to t.key
|
||||
inner := map[string]interface{}{}
|
||||
if len(list) > i {
|
||||
var ok bool
|
||||
inner, ok = list[i].(map[string]interface{})
|
||||
if !ok {
|
||||
// We have indices out of order. Initialize empty value.
|
||||
list[i] = map[string]interface{}{}
|
||||
inner = list[i].(map[string]interface{})
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse
|
||||
e := t.key(inner, nestedNameLevel)
|
||||
if e != nil {
|
||||
return list, e
|
||||
}
|
||||
return setIndex(list, i, inner)
|
||||
default:
|
||||
return nil, errors.Errorf("parse error: unexpected token %v", last)
|
||||
}
|
||||
}
|
||||
|
||||
// check for an empty value
|
||||
// read and consume optional spaces until comma or EOF (empty val) or any other char (not empty val)
|
||||
// comma and spaces are consumed, while any other char is not cosumed
|
||||
func (t *parser) emptyVal() (bool, error) {
|
||||
for {
|
||||
r, _, e := t.sc.ReadRune()
|
||||
if e == io.EOF {
|
||||
return true, nil
|
||||
}
|
||||
if e != nil {
|
||||
return false, e
|
||||
}
|
||||
if r == ',' {
|
||||
return true, nil
|
||||
}
|
||||
if !unicode.IsSpace(r) {
|
||||
t.sc.UnreadRune()
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *parser) val() ([]rune, error) {
|
||||
stop := runeSet([]rune{','})
|
||||
v, _, err := runesUntil(t.sc, stop)
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (t *parser) valList() ([]interface{}, error) {
|
||||
r, _, e := t.sc.ReadRune()
|
||||
if e != nil {
|
||||
return []interface{}{}, e
|
||||
}
|
||||
|
||||
if r != '{' {
|
||||
t.sc.UnreadRune()
|
||||
return []interface{}{}, ErrNotList
|
||||
}
|
||||
|
||||
list := []interface{}{}
|
||||
stop := runeSet([]rune{',', '}'})
|
||||
for {
|
||||
switch rs, last, err := runesUntil(t.sc, stop); {
|
||||
case err != nil:
|
||||
if err == io.EOF {
|
||||
err = errors.New("list must terminate with '}'")
|
||||
}
|
||||
return list, err
|
||||
case last == '}':
|
||||
// If this is followed by ',', consume it.
|
||||
if r, _, e := t.sc.ReadRune(); e == nil && r != ',' {
|
||||
t.sc.UnreadRune()
|
||||
}
|
||||
v, e := t.reader(rs)
|
||||
list = append(list, v)
|
||||
return list, e
|
||||
case last == ',':
|
||||
v, e := t.reader(rs)
|
||||
if e != nil {
|
||||
return list, e
|
||||
}
|
||||
list = append(list, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runesUntil(in io.RuneReader, stop map[rune]bool) ([]rune, rune, error) {
|
||||
v := []rune{}
|
||||
for {
|
||||
switch r, _, e := in.ReadRune(); {
|
||||
case e != nil:
|
||||
return v, r, e
|
||||
case inMap(r, stop):
|
||||
return v, r, nil
|
||||
case r == '\\':
|
||||
next, _, e := in.ReadRune()
|
||||
if e != nil {
|
||||
return v, next, e
|
||||
}
|
||||
v = append(v, next)
|
||||
default:
|
||||
v = append(v, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func inMap(k rune, m map[rune]bool) bool {
|
||||
_, ok := m[k]
|
||||
return ok
|
||||
}
|
||||
|
||||
func typedVal(v []rune, st bool) interface{} {
|
||||
val := string(v)
|
||||
|
||||
if st {
|
||||
return val
|
||||
}
|
||||
|
||||
if strings.EqualFold(val, "true") {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.EqualFold(val, "false") {
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.EqualFold(val, "null") {
|
||||
return nil
|
||||
}
|
||||
|
||||
if strings.EqualFold(val, "0") {
|
||||
return int64(0)
|
||||
}
|
||||
|
||||
// If this value does not start with zero, try parsing it to an int
|
||||
if len(val) != 0 && val[0] != '0' {
|
||||
if iv, err := strconv.ParseInt(val, 10, 64); err == nil {
|
||||
return iv
|
||||
}
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
3
vendor/helm.sh/helm/v3/pkg/uploader/doc.go
vendored
3
vendor/helm.sh/helm/v3/pkg/uploader/doc.go
vendored
@@ -13,7 +13,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*Package uploader provides a library for uploading charts.
|
||||
/*
|
||||
Package uploader provides a library for uploading charts.
|
||||
|
||||
This package contains tools for uploading charts to registries.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user