420
vendor/helm.sh/helm/v3/pkg/action/action.go
vendored
Normal file
420
vendor/helm.sh/helm/v3/pkg/action/action.go
vendored
Normal file
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/engine"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/postrender"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
"helm.sh/helm/v3/pkg/storage"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
"helm.sh/helm/v3/pkg/time"
|
||||
)
|
||||
|
||||
// Timestamper is a function capable of producing a timestamp.Timestamper.
|
||||
//
|
||||
// By default, this is a time.Time function from the Helm time package. This can
|
||||
// be overridden for testing though, so that timestamps are predictable.
|
||||
var Timestamper = time.Now
|
||||
|
||||
var (
|
||||
// errMissingChart indicates that a chart was not provided.
|
||||
errMissingChart = errors.New("no chart provided")
|
||||
// errMissingRelease indicates that a release (name) was not provided.
|
||||
errMissingRelease = errors.New("no release provided")
|
||||
// errInvalidRevision indicates that an invalid release revision number was provided.
|
||||
errInvalidRevision = errors.New("invalid release revision")
|
||||
// errPending indicates that another instance of Helm is already applying an operation on a release.
|
||||
errPending = errors.New("another operation (install/upgrade/rollback) is in progress")
|
||||
)
|
||||
|
||||
// ValidName is a regular expression for resource names.
|
||||
//
|
||||
// DEPRECATED: This will be removed in Helm 4, and is no longer used here. See
|
||||
// pkg/lint/rules.validateMetadataNameFunc for the replacement.
|
||||
//
|
||||
// According to the Kubernetes help text, the regular expression it uses is:
|
||||
//
|
||||
// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
|
||||
//
|
||||
// This follows the above regular expression (but requires a full string match, not partial).
|
||||
//
|
||||
// The Kubernetes documentation is here, though it is not entirely correct:
|
||||
// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
var ValidName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)
|
||||
|
||||
// Configuration injects the dependencies that all actions share.
|
||||
type Configuration struct {
|
||||
// RESTClientGetter is an interface that loads Kubernetes clients.
|
||||
RESTClientGetter RESTClientGetter
|
||||
|
||||
// Releases stores records of releases.
|
||||
Releases *storage.Storage
|
||||
|
||||
// KubeClient is a Kubernetes API client.
|
||||
KubeClient kube.Interface
|
||||
|
||||
// RegistryClient is a client for working with registries
|
||||
RegistryClient *registry.Client
|
||||
|
||||
// Capabilities describes the capabilities of the Kubernetes cluster.
|
||||
Capabilities *chartutil.Capabilities
|
||||
|
||||
Log func(string, ...interface{})
|
||||
}
|
||||
|
||||
// renderResources renders the templates in a chart
|
||||
//
|
||||
// TODO: This function is badly in need of a refactor.
|
||||
// TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed
|
||||
// This code has to do with writing files to disk.
|
||||
func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) {
|
||||
hs := []*release.Hook{}
|
||||
b := bytes.NewBuffer(nil)
|
||||
|
||||
caps, err := c.getCapabilities()
|
||||
if err != nil {
|
||||
return hs, b, "", err
|
||||
}
|
||||
|
||||
if ch.Metadata.KubeVersion != "" {
|
||||
if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) {
|
||||
return hs, b, "", errors.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String())
|
||||
}
|
||||
}
|
||||
|
||||
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 && c.RESTClientGetter != nil {
|
||||
rest, err := c.RESTClientGetter.ToRESTConfig()
|
||||
if err != nil {
|
||||
return hs, b, "", err
|
||||
}
|
||||
files, err2 = engine.RenderWithClient(ch, values, rest)
|
||||
} else {
|
||||
files, err2 = engine.Render(ch, values)
|
||||
}
|
||||
|
||||
if err2 != nil {
|
||||
return hs, b, "", err2
|
||||
}
|
||||
|
||||
// NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource,
|
||||
// pull it out of here into a separate file so that we can actually use the output of the rendered
|
||||
// text file. We have to spin through this map because the file contains path information, so we
|
||||
// look for terminating NOTES.txt. We also remove it from the files so that we don't have to skip
|
||||
// it in the sortHooks.
|
||||
var notesBuffer bytes.Buffer
|
||||
for k, v := range files {
|
||||
if strings.HasSuffix(k, notesFileSuffix) {
|
||||
if subNotes || (k == path.Join(ch.Name(), "templates", notesFileSuffix)) {
|
||||
// If buffer contains data, add newline before adding more
|
||||
if notesBuffer.Len() > 0 {
|
||||
notesBuffer.WriteString("\n")
|
||||
}
|
||||
notesBuffer.WriteString(v)
|
||||
}
|
||||
delete(files, k)
|
||||
}
|
||||
}
|
||||
notes := notesBuffer.String()
|
||||
|
||||
// Sort hooks, manifests, and partials. Only hooks and manifests are returned,
|
||||
// as partials are not used after renderer.Render. Empty manifests are also
|
||||
// removed here.
|
||||
hs, manifests, err := releaseutil.SortManifests(files, caps.APIVersions, releaseutil.InstallOrder)
|
||||
if err != nil {
|
||||
// By catching parse errors here, we can prevent bogus releases from going
|
||||
// to Kubernetes.
|
||||
//
|
||||
// We return the files as a big blob of data to help the user debug parser
|
||||
// errors.
|
||||
for name, content := range files {
|
||||
if strings.TrimSpace(content) == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(b, "---\n# Source: %s\n%s\n", name, content)
|
||||
}
|
||||
return hs, b, "", err
|
||||
}
|
||||
|
||||
// Aggregate all valid manifests into one big doc.
|
||||
fileWritten := make(map[string]bool)
|
||||
|
||||
if includeCrds {
|
||||
for _, crd := range ch.CRDObjects() {
|
||||
if outputDir == "" {
|
||||
fmt.Fprintf(b, "---\n# Source: %s\n%s\n", crd.Name, string(crd.File.Data[:]))
|
||||
} else {
|
||||
err = writeToFile(outputDir, crd.Filename, string(crd.File.Data[:]), fileWritten[crd.Name])
|
||||
if err != nil {
|
||||
return hs, b, "", err
|
||||
}
|
||||
fileWritten[crd.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range manifests {
|
||||
if outputDir == "" {
|
||||
fmt.Fprintf(b, "---\n# Source: %s\n%s\n", m.Name, m.Content)
|
||||
} else {
|
||||
newDir := outputDir
|
||||
if useReleaseName {
|
||||
newDir = filepath.Join(outputDir, releaseName)
|
||||
}
|
||||
// NOTE: We do not have to worry about the post-renderer because
|
||||
// output dir is only used by `helm template`. In the next major
|
||||
// release, we should move this logic to template only as it is not
|
||||
// used by install or upgrade
|
||||
err = writeToFile(newDir, m.Name, m.Content, fileWritten[m.Name])
|
||||
if err != nil {
|
||||
return hs, b, "", err
|
||||
}
|
||||
fileWritten[m.Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
if pr != nil {
|
||||
b, err = pr.Run(b)
|
||||
if err != nil {
|
||||
return hs, b, notes, errors.Wrap(err, "error while running post render on files")
|
||||
}
|
||||
}
|
||||
|
||||
return hs, b, notes, nil
|
||||
}
|
||||
|
||||
// RESTClientGetter gets the rest client
|
||||
type RESTClientGetter interface {
|
||||
ToRESTConfig() (*rest.Config, error)
|
||||
ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error)
|
||||
ToRESTMapper() (meta.RESTMapper, error)
|
||||
}
|
||||
|
||||
// DebugLog sets the logger that writes debug strings
|
||||
type DebugLog func(format string, v ...interface{})
|
||||
|
||||
// capabilities builds a Capabilities from discovery information.
|
||||
func (c *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
|
||||
if c.Capabilities != nil {
|
||||
return c.Capabilities, nil
|
||||
}
|
||||
dc, err := c.RESTClientGetter.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get Kubernetes discovery client")
|
||||
}
|
||||
// force a discovery cache invalidation to always fetch the latest server version/capabilities.
|
||||
dc.Invalidate()
|
||||
kubeVersion, err := dc.ServerVersion()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get server version from Kubernetes")
|
||||
}
|
||||
// Issue #6361:
|
||||
// Client-Go emits an error when an API service is registered but unimplemented.
|
||||
// We trap that error here and print a warning. But since the discovery client continues
|
||||
// building the API object, it is correctly populated with all valid APIs.
|
||||
// See https://github.com/kubernetes/kubernetes/issues/72051#issuecomment-521157642
|
||||
apiVersions, err := GetVersionSet(dc)
|
||||
if err != nil {
|
||||
if discovery.IsGroupDiscoveryFailedError(err) {
|
||||
c.Log("WARNING: The Kubernetes server has an orphaned API service. Server reports: %s", err)
|
||||
c.Log("WARNING: To fix this, kubectl delete apiservice <service-name>")
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "could not get apiVersions from Kubernetes")
|
||||
}
|
||||
}
|
||||
|
||||
c.Capabilities = &chartutil.Capabilities{
|
||||
APIVersions: apiVersions,
|
||||
KubeVersion: chartutil.KubeVersion{
|
||||
Version: kubeVersion.GitVersion,
|
||||
Major: kubeVersion.Major,
|
||||
Minor: kubeVersion.Minor,
|
||||
},
|
||||
}
|
||||
return c.Capabilities, nil
|
||||
}
|
||||
|
||||
// KubernetesClientSet creates a new kubernetes ClientSet based on the configuration
|
||||
func (c *Configuration) KubernetesClientSet() (kubernetes.Interface, error) {
|
||||
conf, err := c.RESTClientGetter.ToRESTConfig()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to generate config for kubernetes client")
|
||||
}
|
||||
|
||||
return kubernetes.NewForConfig(conf)
|
||||
}
|
||||
|
||||
// Now generates a timestamp
|
||||
//
|
||||
// If the configuration has a Timestamper on it, that will be used.
|
||||
// Otherwise, this will use time.Now().
|
||||
func (c *Configuration) Now() time.Time {
|
||||
return Timestamper()
|
||||
}
|
||||
|
||||
func (c *Configuration) releaseContent(name string, version int) (*release.Release, error) {
|
||||
if err := chartutil.ValidateReleaseName(name); err != nil {
|
||||
return nil, errors.Errorf("releaseContent: Release name is invalid: %s", name)
|
||||
}
|
||||
|
||||
if version <= 0 {
|
||||
return c.Releases.Last(name)
|
||||
}
|
||||
|
||||
return c.Releases.Get(name, version)
|
||||
}
|
||||
|
||||
// GetVersionSet retrieves a set of available k8s API versions
|
||||
func GetVersionSet(client discovery.ServerResourcesInterface) (chartutil.VersionSet, error) {
|
||||
groups, resources, err := client.ServerGroupsAndResources()
|
||||
if err != nil && !discovery.IsGroupDiscoveryFailedError(err) {
|
||||
return chartutil.DefaultVersionSet, errors.Wrap(err, "could not get apiVersions from Kubernetes")
|
||||
}
|
||||
|
||||
// FIXME: The Kubernetes test fixture for cli appears to always return nil
|
||||
// for calls to Discovery().ServerGroupsAndResources(). So in this case, we
|
||||
// return the default API list. This is also a safe value to return in any
|
||||
// other odd-ball case.
|
||||
if len(groups) == 0 && len(resources) == 0 {
|
||||
return chartutil.DefaultVersionSet, nil
|
||||
}
|
||||
|
||||
versionMap := make(map[string]interface{})
|
||||
versions := []string{}
|
||||
|
||||
// Extract the groups
|
||||
for _, g := range groups {
|
||||
for _, gv := range g.Versions {
|
||||
versionMap[gv.GroupVersion] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the resources
|
||||
var id string
|
||||
var ok bool
|
||||
for _, r := range resources {
|
||||
for _, rl := range r.APIResources {
|
||||
|
||||
// A Kind at a GroupVersion can show up more than once. We only want
|
||||
// it displayed once in the final output.
|
||||
id = path.Join(r.GroupVersion, rl.Kind)
|
||||
if _, ok = versionMap[id]; !ok {
|
||||
versionMap[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to a form that NewVersionSet can use
|
||||
for k := range versionMap {
|
||||
versions = append(versions, k)
|
||||
}
|
||||
|
||||
return chartutil.VersionSet(versions), nil
|
||||
}
|
||||
|
||||
// recordRelease with an update operation in case reuse has been set.
|
||||
func (c *Configuration) recordRelease(r *release.Release) {
|
||||
if err := c.Releases.Update(r); err != nil {
|
||||
c.Log("warning: Failed to update release %s: %s", r.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes the action configuration
|
||||
func (c *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace, helmDriver string, log DebugLog) error {
|
||||
kc := kube.New(getter)
|
||||
kc.Log = log
|
||||
|
||||
lazyClient := &lazyClient{
|
||||
namespace: namespace,
|
||||
clientFn: kc.Factory.KubernetesClientSet,
|
||||
}
|
||||
|
||||
var store *storage.Storage
|
||||
switch helmDriver {
|
||||
case "secret", "secrets", "":
|
||||
d := driver.NewSecrets(newSecretClient(lazyClient))
|
||||
d.Log = log
|
||||
store = storage.Init(d)
|
||||
case "configmap", "configmaps":
|
||||
d := driver.NewConfigMaps(newConfigMapClient(lazyClient))
|
||||
d.Log = log
|
||||
store = storage.Init(d)
|
||||
case "memory":
|
||||
var d *driver.Memory
|
||||
if c.Releases != nil {
|
||||
if mem, ok := c.Releases.Driver.(*driver.Memory); ok {
|
||||
// This function can be called more than once (e.g., helm list --all-namespaces).
|
||||
// If a memory driver was already initialized, re-use it but set the possibly new namespace.
|
||||
// We re-use it in case some releases where already created in the existing memory driver.
|
||||
d = mem
|
||||
}
|
||||
}
|
||||
if d == nil {
|
||||
d = driver.NewMemory()
|
||||
}
|
||||
d.SetNamespace(namespace)
|
||||
store = storage.Init(d)
|
||||
case "sql":
|
||||
d, err := driver.NewSQL(
|
||||
os.Getenv("HELM_DRIVER_SQL_CONNECTION_STRING"),
|
||||
log,
|
||||
namespace,
|
||||
)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Unable to instantiate SQL driver: %v", err))
|
||||
}
|
||||
store = storage.Init(d)
|
||||
default:
|
||||
// Not sure what to do here.
|
||||
panic("Unknown driver in HELM_DRIVER: " + helmDriver)
|
||||
}
|
||||
|
||||
c.RESTClientGetter = getter
|
||||
c.KubeClient = kc
|
||||
c.Releases = store
|
||||
c.Log = log
|
||||
|
||||
return nil
|
||||
}
|
||||
63
vendor/helm.sh/helm/v3/pkg/action/chart_export.go
vendored
Normal file
63
vendor/helm.sh/helm/v3/pkg/action/chart_export.go
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
)
|
||||
|
||||
// ChartExport performs a chart export operation.
|
||||
type ChartExport struct {
|
||||
cfg *Configuration
|
||||
|
||||
Destination string
|
||||
}
|
||||
|
||||
// NewChartExport creates a new ChartExport object with the given configuration.
|
||||
func NewChartExport(cfg *Configuration) *ChartExport {
|
||||
return &ChartExport{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart export operation
|
||||
func (a *ChartExport) Run(out io.Writer, ref string) error {
|
||||
r, err := registry.ParseReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch, err := a.cfg.RegistryClient.LoadChart(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save the chart to local destination directory
|
||||
err = chartutil.SaveDir(ch, a.Destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d := filepath.Join(a.Destination, ch.Metadata.Name)
|
||||
fmt.Fprintf(out, "Exported chart to %s/\n", d)
|
||||
return nil
|
||||
}
|
||||
38
vendor/helm.sh/helm/v3/pkg/action/chart_list.go
vendored
Normal file
38
vendor/helm.sh/helm/v3/pkg/action/chart_list.go
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// ChartList performs a chart list operation.
|
||||
type ChartList struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewChartList creates a new ChartList object with the given configuration.
|
||||
func NewChartList(cfg *Configuration) *ChartList {
|
||||
return &ChartList{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart list operation
|
||||
func (a *ChartList) Run(out io.Writer) error {
|
||||
return a.cfg.RegistryClient.PrintChartTable()
|
||||
}
|
||||
44
vendor/helm.sh/helm/v3/pkg/action/chart_pull.go
vendored
Normal file
44
vendor/helm.sh/helm/v3/pkg/action/chart_pull.go
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
)
|
||||
|
||||
// ChartPull performs a chart pull operation.
|
||||
type ChartPull struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewChartPull creates a new ChartPull object with the given configuration.
|
||||
func NewChartPull(cfg *Configuration) *ChartPull {
|
||||
return &ChartPull{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart pull operation
|
||||
func (a *ChartPull) Run(out io.Writer, ref string) error {
|
||||
r, err := registry.ParseReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.cfg.RegistryClient.PullChartToCache(r)
|
||||
}
|
||||
44
vendor/helm.sh/helm/v3/pkg/action/chart_push.go
vendored
Normal file
44
vendor/helm.sh/helm/v3/pkg/action/chart_push.go
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
)
|
||||
|
||||
// ChartPush performs a chart push operation.
|
||||
type ChartPush struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewChartPush creates a new ChartPush object with the given configuration.
|
||||
func NewChartPush(cfg *Configuration) *ChartPush {
|
||||
return &ChartPush{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart push operation
|
||||
func (a *ChartPush) Run(out io.Writer, ref string) error {
|
||||
r, err := registry.ParseReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.cfg.RegistryClient.PushChart(r)
|
||||
}
|
||||
44
vendor/helm.sh/helm/v3/pkg/action/chart_remove.go
vendored
Normal file
44
vendor/helm.sh/helm/v3/pkg/action/chart_remove.go
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
)
|
||||
|
||||
// ChartRemove performs a chart remove operation.
|
||||
type ChartRemove struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewChartRemove creates a new ChartRemove object with the given configuration.
|
||||
func NewChartRemove(cfg *Configuration) *ChartRemove {
|
||||
return &ChartRemove{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart remove operation
|
||||
func (a *ChartRemove) Run(out io.Writer, ref string) error {
|
||||
r, err := registry.ParseReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.cfg.RegistryClient.RemoveChart(r)
|
||||
}
|
||||
51
vendor/helm.sh/helm/v3/pkg/action/chart_save.go
vendored
Normal file
51
vendor/helm.sh/helm/v3/pkg/action/chart_save.go
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"helm.sh/helm/v3/internal/experimental/registry"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
)
|
||||
|
||||
// ChartSave performs a chart save operation.
|
||||
type ChartSave struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewChartSave creates a new ChartSave object with the given configuration.
|
||||
func NewChartSave(cfg *Configuration) *ChartSave {
|
||||
return &ChartSave{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the chart save operation
|
||||
func (a *ChartSave) Run(out io.Writer, ch *chart.Chart, ref string) error {
|
||||
r, err := registry.ParseReference(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If no tag is present, use the chart version
|
||||
if r.Tag == "" {
|
||||
r.Tag = ch.Metadata.Version
|
||||
}
|
||||
|
||||
return a.cfg.RegistryClient.SaveChart(ch, r)
|
||||
}
|
||||
227
vendor/helm.sh/helm/v3/pkg/action/dependency.go
vendored
Normal file
227
vendor/helm.sh/helm/v3/pkg/action/dependency.go
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/gosuri/uitable"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
)
|
||||
|
||||
// Dependency is the action for building a given chart's dependency tree.
|
||||
//
|
||||
// It provides the implementation of 'helm dependency' and its respective subcommands.
|
||||
type Dependency struct {
|
||||
Verify bool
|
||||
Keyring string
|
||||
SkipRefresh bool
|
||||
}
|
||||
|
||||
// NewDependency creates a new Dependency object with the given configuration.
|
||||
func NewDependency() *Dependency {
|
||||
return &Dependency{}
|
||||
}
|
||||
|
||||
// List executes 'helm dependency list'.
|
||||
func (d *Dependency) List(chartpath string, out io.Writer) error {
|
||||
c, err := loader.Load(chartpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Metadata.Dependencies == nil {
|
||||
fmt.Fprintf(out, "WARNING: no dependencies at %s\n", filepath.Join(chartpath, "charts"))
|
||||
return nil
|
||||
}
|
||||
|
||||
d.printDependencies(chartpath, out, c)
|
||||
fmt.Fprintln(out)
|
||||
d.printMissing(chartpath, out, c.Metadata.Dependencies)
|
||||
return nil
|
||||
}
|
||||
|
||||
// dependencyStatus returns a string describing the status of a dependency viz a viz the parent chart.
|
||||
func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, parent *chart.Chart) string {
|
||||
filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*")
|
||||
|
||||
// If a chart is unpacked, this will check the unpacked chart's `charts/` directory for tarballs.
|
||||
// Technically, this is COMPLETELY unnecessary, and should be removed in Helm 4. It is here
|
||||
// to preserved backward compatibility. In Helm 2/3, there is a "difference" between
|
||||
// the tgz version (which outputs "ok" if it unpacks) and the loaded version (which outputs
|
||||
// "unpacked"). Early in Helm 2's history, this would have made a difference. But it no
|
||||
// longer does. However, since this code shipped with Helm 3, the output must remain stable
|
||||
// until Helm 4.
|
||||
switch archives, err := filepath.Glob(filepath.Join(chartpath, "charts", filename)); {
|
||||
case err != nil:
|
||||
return "bad pattern"
|
||||
case len(archives) > 1:
|
||||
// See if the second part is a SemVer
|
||||
found := []string{}
|
||||
for _, arc := range archives {
|
||||
// we need to trip the prefix dirs and the extension off.
|
||||
filename = strings.TrimSuffix(filepath.Base(arc), ".tgz")
|
||||
maybeVersion := strings.TrimPrefix(filename, fmt.Sprintf("%s-", dep.Name))
|
||||
|
||||
if _, err := semver.StrictNewVersion(maybeVersion); err == nil {
|
||||
// If the version parsed without an error, it is possibly a valid
|
||||
// version.
|
||||
found = append(found, arc)
|
||||
}
|
||||
}
|
||||
|
||||
if l := len(found); l == 1 {
|
||||
// If we get here, we do the same thing as in len(archives) == 1.
|
||||
if r := statArchiveForStatus(found[0], dep); r != "" {
|
||||
return r
|
||||
}
|
||||
|
||||
// Fall through and look for directories
|
||||
} else if l > 1 {
|
||||
return "too many matches"
|
||||
}
|
||||
|
||||
// The sanest thing to do here is to fall through and see if we have any directory
|
||||
// matches.
|
||||
|
||||
case len(archives) == 1:
|
||||
archive := archives[0]
|
||||
if r := statArchiveForStatus(archive, dep); r != "" {
|
||||
return r
|
||||
}
|
||||
|
||||
}
|
||||
// End unnecessary code.
|
||||
|
||||
var depChart *chart.Chart
|
||||
for _, item := range parent.Dependencies() {
|
||||
if item.Name() == dep.Name {
|
||||
depChart = item
|
||||
}
|
||||
}
|
||||
|
||||
if depChart == nil {
|
||||
return "missing"
|
||||
}
|
||||
|
||||
if depChart.Metadata.Version != dep.Version {
|
||||
constraint, err := semver.NewConstraint(dep.Version)
|
||||
if err != nil {
|
||||
return "invalid version"
|
||||
}
|
||||
|
||||
v, err := semver.NewVersion(depChart.Metadata.Version)
|
||||
if err != nil {
|
||||
return "invalid version"
|
||||
}
|
||||
|
||||
if !constraint.Check(v) {
|
||||
return "wrong version"
|
||||
}
|
||||
}
|
||||
|
||||
return "unpacked"
|
||||
}
|
||||
|
||||
// stat an archive and return a message if the stat is successful
|
||||
//
|
||||
// This is a refactor of the code originally in dependencyStatus. It is here to
|
||||
// support legacy behavior, and should be removed in Helm 4.
|
||||
func statArchiveForStatus(archive string, dep *chart.Dependency) string {
|
||||
if _, err := os.Stat(archive); err == nil {
|
||||
c, err := loader.Load(archive)
|
||||
if err != nil {
|
||||
return "corrupt"
|
||||
}
|
||||
if c.Name() != dep.Name {
|
||||
return "misnamed"
|
||||
}
|
||||
|
||||
if c.Metadata.Version != dep.Version {
|
||||
constraint, err := semver.NewConstraint(dep.Version)
|
||||
if err != nil {
|
||||
return "invalid version"
|
||||
}
|
||||
|
||||
v, err := semver.NewVersion(c.Metadata.Version)
|
||||
if err != nil {
|
||||
return "invalid version"
|
||||
}
|
||||
|
||||
if !constraint.Check(v) {
|
||||
return "wrong version"
|
||||
}
|
||||
}
|
||||
return "ok"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// printDependencies prints all of the dependencies in the yaml file.
|
||||
func (d *Dependency) printDependencies(chartpath string, out io.Writer, c *chart.Chart) {
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = 80
|
||||
table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS")
|
||||
for _, row := range c.Metadata.Dependencies {
|
||||
table.AddRow(row.Name, row.Version, row.Repository, d.dependencyStatus(chartpath, row, c))
|
||||
}
|
||||
fmt.Fprintln(out, table)
|
||||
}
|
||||
|
||||
// printMissing prints warnings about charts that are present on disk, but are
|
||||
// not in Charts.yaml.
|
||||
func (d *Dependency) printMissing(chartpath string, out io.Writer, reqs []*chart.Dependency) {
|
||||
folder := filepath.Join(chartpath, "charts/*")
|
||||
files, err := filepath.Glob(folder)
|
||||
if err != nil {
|
||||
fmt.Fprintln(out, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
fi, err := os.Stat(f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "Warning: %s\n", err)
|
||||
}
|
||||
// Skip anything that is not a directory and not a tgz file.
|
||||
if !fi.IsDir() && filepath.Ext(f) != ".tgz" {
|
||||
continue
|
||||
}
|
||||
c, err := loader.Load(f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "WARNING: %q is not a chart.\n", f)
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
for _, d := range reqs {
|
||||
if d.Name == c.Name() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
fmt.Fprintf(out, "WARNING: %q is not in Chart.yaml.\n", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
22
vendor/helm.sh/helm/v3/pkg/action/doc.go
vendored
Normal file
22
vendor/helm.sh/helm/v3/pkg/action/doc.go
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
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 contains the logic for each action that Helm can perform.
|
||||
//
|
||||
// This is a library for calling top-level Helm actions like 'install',
|
||||
// 'upgrade', or 'list'. Actions approximately match the command line
|
||||
// invocations that the Helm client uses.
|
||||
package action
|
||||
47
vendor/helm.sh/helm/v3/pkg/action/get.go
vendored
Normal file
47
vendor/helm.sh/helm/v3/pkg/action/get.go
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
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 (
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
// Get is the action for checking a given release's information.
|
||||
//
|
||||
// It provides the implementation of 'helm get' and its respective subcommands (except `helm get values`).
|
||||
type Get struct {
|
||||
cfg *Configuration
|
||||
|
||||
// Initializing Version to 0 will get the latest revision of the release.
|
||||
Version int
|
||||
}
|
||||
|
||||
// NewGet creates a new Get object with the given configuration.
|
||||
func NewGet(cfg *Configuration) *Get {
|
||||
return &Get{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes 'helm get' against the given release.
|
||||
func (g *Get) Run(name string) (*release.Release, error) {
|
||||
if err := g.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return g.cfg.releaseContent(name, g.Version)
|
||||
}
|
||||
60
vendor/helm.sh/helm/v3/pkg/action/get_values.go
vendored
Normal file
60
vendor/helm.sh/helm/v3/pkg/action/get_values.go
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 (
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
)
|
||||
|
||||
// GetValues is the action for checking a given release's values.
|
||||
//
|
||||
// It provides the implementation of 'helm get values'.
|
||||
type GetValues struct {
|
||||
cfg *Configuration
|
||||
|
||||
Version int
|
||||
AllValues bool
|
||||
}
|
||||
|
||||
// NewGetValues creates a new GetValues object with the given configuration.
|
||||
func NewGetValues(cfg *Configuration) *GetValues {
|
||||
return &GetValues{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes 'helm get values' against the given release.
|
||||
func (g *GetValues) Run(name string) (map[string]interface{}, 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
|
||||
}
|
||||
|
||||
// If the user wants all values, compute the values and return.
|
||||
if g.AllValues {
|
||||
cfg, err := chartutil.CoalesceValues(rel.Chart, rel.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
return rel.Config, nil
|
||||
}
|
||||
58
vendor/helm.sh/helm/v3/pkg/action/history.go
vendored
Normal file
58
vendor/helm.sh/helm/v3/pkg/action/history.go
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
// History is the action for checking the release's ledger.
|
||||
//
|
||||
// It provides the implementation of 'helm history'.
|
||||
// It returns all the revisions for a specific release.
|
||||
// To list up to one revision of every release in one specific, or in all,
|
||||
// namespaces, see the List action.
|
||||
type History struct {
|
||||
cfg *Configuration
|
||||
|
||||
Max int
|
||||
Version int
|
||||
}
|
||||
|
||||
// NewHistory creates a new History object with the given configuration.
|
||||
func NewHistory(cfg *Configuration) *History {
|
||||
return &History{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes 'helm history' against the given release.
|
||||
func (h *History) Run(name string) ([]*release.Release, error) {
|
||||
if err := h.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := chartutil.ValidateReleaseName(name); err != nil {
|
||||
return nil, errors.Errorf("release name is invalid: %s", name)
|
||||
}
|
||||
|
||||
h.cfg.Log("getting history for release %s", name)
|
||||
return h.cfg.Releases.History(name)
|
||||
}
|
||||
151
vendor/helm.sh/helm/v3/pkg/action/hooks.go
vendored
Normal file
151
vendor/helm.sh/helm/v3/pkg/action/hooks.go
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
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 (
|
||||
"bytes"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
)
|
||||
|
||||
// execHook executes all of the hooks for the given hook event.
|
||||
func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent, timeout time.Duration) error {
|
||||
executingHooks := []*release.Hook{}
|
||||
|
||||
for _, h := range rl.Hooks {
|
||||
for _, e := range h.Events {
|
||||
if e == hook {
|
||||
executingHooks = append(executingHooks, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hooke are pre-ordered by kind, so keep order stable
|
||||
sort.Stable(hookByWeight(executingHooks))
|
||||
|
||||
for _, h := range executingHooks {
|
||||
// Set default delete policy to before-hook-creation
|
||||
if h.DeletePolicies == nil || len(h.DeletePolicies) == 0 {
|
||||
// TODO(jlegrone): Only apply before-hook-creation delete policy to run to completion
|
||||
// resources. For all other resource types update in place if a
|
||||
// resource with the same name already exists and is owned by the
|
||||
// current release.
|
||||
h.DeletePolicies = []release.HookDeletePolicy{release.HookBeforeHookCreation}
|
||||
}
|
||||
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookBeforeHookCreation); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest), true)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to build kubernetes object for %s hook %s", hook, h.Path)
|
||||
}
|
||||
|
||||
// Record the time at which the hook was applied to the cluster
|
||||
h.LastRun = release.HookExecution{
|
||||
StartedAt: helmtime.Now(),
|
||||
Phase: release.HookPhaseRunning,
|
||||
}
|
||||
cfg.recordRelease(rl)
|
||||
|
||||
// As long as the implementation of WatchUntilReady does not panic, HookPhaseFailed or HookPhaseSucceeded
|
||||
// should always be set by this function. If we fail to do that for any reason, then HookPhaseUnknown is
|
||||
// the most appropriate value to surface.
|
||||
h.LastRun.Phase = release.HookPhaseUnknown
|
||||
|
||||
// Create hook resources
|
||||
if _, err := cfg.KubeClient.Create(resources); err != nil {
|
||||
h.LastRun.CompletedAt = helmtime.Now()
|
||||
h.LastRun.Phase = release.HookPhaseFailed
|
||||
return errors.Wrapf(err, "warning: Hook %s %s failed", hook, h.Path)
|
||||
}
|
||||
|
||||
// Watch hook resources until they have completed
|
||||
err = cfg.KubeClient.WatchUntilReady(resources, timeout)
|
||||
// Note the time of success/failure
|
||||
h.LastRun.CompletedAt = helmtime.Now()
|
||||
// Mark hook as succeeded or failed
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
h.LastRun.Phase = release.HookPhaseSucceeded
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// hookByWeight is a sorter for hooks
|
||||
type hookByWeight []*release.Hook
|
||||
|
||||
func (x hookByWeight) Len() int { return len(x) }
|
||||
func (x hookByWeight) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x hookByWeight) Less(i, j int) bool {
|
||||
if x[i].Weight == x[j].Weight {
|
||||
return x[i].Name < x[j].Name
|
||||
}
|
||||
return x[i].Weight < x[j].Weight
|
||||
}
|
||||
|
||||
// deleteHookByPolicy deletes a hook if the hook policy instructs it to
|
||||
func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy) error {
|
||||
// Never delete CustomResourceDefinitions; this could cause lots of
|
||||
// cascading garbage collection.
|
||||
if h.Kind == "CustomResourceDefinition" {
|
||||
return nil
|
||||
}
|
||||
if hookHasDeletePolicy(h, policy) {
|
||||
resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest), false)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to build kubernetes object for deleting hook %s", h.Path)
|
||||
}
|
||||
_, errs := cfg.KubeClient.Delete(resources)
|
||||
if len(errs) > 0 {
|
||||
return errors.New(joinErrors(errs))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// hookHasDeletePolicy determines whether the defined hook deletion policy matches the hook deletion polices
|
||||
// supported by helm. If so, mark the hook as one should be deleted.
|
||||
func hookHasDeletePolicy(h *release.Hook, policy release.HookDeletePolicy) bool {
|
||||
for _, v := range h.DeletePolicies {
|
||||
if policy == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
720
vendor/helm.sh/helm/v3/pkg/action/install.go
vendored
Normal file
720
vendor/helm.sh/helm/v3/pkg/action/install.go
vendored
Normal file
@@ -0,0 +1,720 @@
|
||||
/*
|
||||
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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"github.com/pkg/errors"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/downloader"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
kubefake "helm.sh/helm/v3/pkg/kube/fake"
|
||||
"helm.sh/helm/v3/pkg/postrender"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
"helm.sh/helm/v3/pkg/storage"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
)
|
||||
|
||||
// releaseNameMaxLen is the maximum length of a release name.
|
||||
//
|
||||
// As of Kubernetes 1.4, the max limit on a name is 63 chars. We reserve 10 for
|
||||
// charts to add data. Effectively, that gives us 53 chars.
|
||||
// See https://github.com/helm/helm/issues/1528
|
||||
const releaseNameMaxLen = 53
|
||||
|
||||
// NOTESFILE_SUFFIX that we want to treat special. It goes through the templating engine
|
||||
// but it's not a yaml file (resource) hence can't have hooks, etc. And the user actually
|
||||
// wants to see this file after rendering in the status command. However, it must be a suffix
|
||||
// since there can be filepath in front of it.
|
||||
const notesFileSuffix = "NOTES.txt"
|
||||
|
||||
const defaultDirectoryPermission = 0755
|
||||
|
||||
// Install performs an installation operation.
|
||||
type Install struct {
|
||||
cfg *Configuration
|
||||
|
||||
ChartPathOptions
|
||||
|
||||
ClientOnly bool
|
||||
CreateNamespace bool
|
||||
DryRun bool
|
||||
DisableHooks bool
|
||||
Replace bool
|
||||
Wait bool
|
||||
WaitForJobs bool
|
||||
Devel bool
|
||||
DependencyUpdate bool
|
||||
Timeout time.Duration
|
||||
Namespace string
|
||||
ReleaseName string
|
||||
GenerateName bool
|
||||
NameTemplate string
|
||||
Description string
|
||||
OutputDir string
|
||||
Atomic bool
|
||||
SkipCRDs bool
|
||||
SubNotes bool
|
||||
DisableOpenAPIValidation bool
|
||||
IncludeCRDs bool
|
||||
// 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
|
||||
KubeVersion *chartutil.KubeVersion
|
||||
APIVersions chartutil.VersionSet
|
||||
// Used by helm template to render charts with .Release.IsUpgrade. Ignored if Dry-Run is false
|
||||
IsUpgrade bool
|
||||
// Used by helm template to add the release as part of OutputDir path
|
||||
// OutputDir/<ReleaseName>
|
||||
UseReleaseName bool
|
||||
PostRenderer postrender.PostRenderer
|
||||
}
|
||||
|
||||
// ChartPathOptions captures common options used for controlling chart paths
|
||||
type ChartPathOptions struct {
|
||||
CaFile string // --ca-file
|
||||
CertFile string // --cert-file
|
||||
KeyFile string // --key-file
|
||||
InsecureSkipTLSverify bool // --insecure-skip-verify
|
||||
Keyring string // --keyring
|
||||
Password string // --password
|
||||
PassCredentialsAll bool // --pass-credentials
|
||||
RepoURL string // --repo
|
||||
Username string // --username
|
||||
Verify bool // --verify
|
||||
Version string // --version
|
||||
}
|
||||
|
||||
// NewInstall creates a new Install object with the given configuration.
|
||||
func NewInstall(cfg *Configuration) *Install {
|
||||
return &Install{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
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{}
|
||||
for _, obj := range crds {
|
||||
// Read in the resources
|
||||
res, err := i.cfg.KubeClient.Build(bytes.NewBuffer(obj.File.Data), false)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to install CRD %s", obj.Name)
|
||||
}
|
||||
|
||||
// Send them to Kube
|
||||
if _, err := i.cfg.KubeClient.Create(res); err != nil {
|
||||
// If the error is CRD already exists, continue.
|
||||
if apierrors.IsAlreadyExists(err) {
|
||||
crdName := res[0].Name
|
||||
i.cfg.Log("CRD %s is already present. Skipping.", crdName)
|
||||
continue
|
||||
}
|
||||
return errors.Wrapf(err, "failed to install CRD %s", obj.Name)
|
||||
}
|
||||
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()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes the installation
|
||||
//
|
||||
// If DryRun is set to true, this will prepare the release, but not install it
|
||||
func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.Release, error) {
|
||||
// Check reachability of cluster unless in client-only mode (e.g. `helm template` without `--validate`)
|
||||
if !i.ClientOnly {
|
||||
if err := i.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := i.availableName(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Pre-install anything in the crd/ directory. We do this before Helm
|
||||
// contacts the upstream server and builds the capabilities object.
|
||||
if crds := chrt.CRDObjects(); !i.ClientOnly && !i.SkipCRDs && len(crds) > 0 {
|
||||
// On dry run, bail here
|
||||
if i.DryRun {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if i.ClientOnly {
|
||||
// Add mock objects in here so it doesn't use Kube API server
|
||||
// NOTE(bacongobbler): used for `helm template`
|
||||
i.cfg.Capabilities = chartutil.DefaultCapabilities.Copy()
|
||||
if i.KubeVersion != nil {
|
||||
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}
|
||||
|
||||
mem := driver.NewMemory()
|
||||
mem.SetNamespace(i.Namespace)
|
||||
i.cfg.Releases = storage.Init(mem)
|
||||
} else if !i.ClientOnly && len(i.APIVersions) > 0 {
|
||||
i.cfg.Log("API Version list given outside of client only mode, this list will be ignored")
|
||||
}
|
||||
|
||||
if err := chartutil.ProcessDependencies(chrt, vals); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure if Atomic is set, that wait is set as well. This makes it so
|
||||
// the user doesn't have to specify both
|
||||
i.Wait = i.Wait || i.Atomic
|
||||
|
||||
caps, err := i.cfg.getCapabilities()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//special case for helm template --is-upgrade
|
||||
isUpgrade := i.IsUpgrade && i.DryRun
|
||||
options := chartutil.ReleaseOptions{
|
||||
Name: i.ReleaseName,
|
||||
Namespace: i.Namespace,
|
||||
Revision: 1,
|
||||
IsInstall: !isUpgrade,
|
||||
IsUpgrade: isUpgrade,
|
||||
}
|
||||
valuesToRender, err := chartutil.ToRenderValues(chrt, vals, options, caps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rel := i.createRelease(chrt, vals)
|
||||
|
||||
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)
|
||||
// Even for errors, attach this if available
|
||||
if manifestDoc != nil {
|
||||
rel.Manifest = manifestDoc.String()
|
||||
}
|
||||
// Check error from render
|
||||
if err != nil {
|
||||
rel.SetStatus(release.StatusFailed, fmt.Sprintf("failed to render resource: %s", err.Error()))
|
||||
// Return a release with partial data so that the client can show debugging information.
|
||||
return rel, err
|
||||
}
|
||||
|
||||
// Mark this release as in-progress
|
||||
rel.SetStatus(release.StatusPendingInstall, "Initial install underway")
|
||||
|
||||
var toBeAdopted kube.ResourceList
|
||||
resources, err := i.cfg.KubeClient.Build(bytes.NewBufferString(rel.Manifest), !i.DisableOpenAPIValidation)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to build kubernetes objects from release manifest")
|
||||
}
|
||||
|
||||
// It is safe to use "force" here because these are resources currently rendered by the chart.
|
||||
err = resources.Visit(setMetadataVisitor(rel.Name, rel.Namespace, true))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Install requires an extra validation step of checking that resources
|
||||
// don't already exist before we actually create resources. If we continue
|
||||
// forward and create the release object with resources that already exist,
|
||||
// we'll end up in a state where we will delete those resources upon
|
||||
// deleting the release because the manifest will be pointing at that
|
||||
// resource
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
// Bail out here if it is a dry run
|
||||
if i.DryRun {
|
||||
rel.Info.Description = "Dry run complete"
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
if i.CreateNamespace {
|
||||
ns := &v1.Namespace{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Namespace",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: i.Namespace,
|
||||
Labels: map[string]string{
|
||||
"name": i.Namespace,
|
||||
},
|
||||
},
|
||||
}
|
||||
buf, err := yaml.Marshal(ns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resourceList, err := i.cfg.KubeClient.Build(bytes.NewBuffer(buf), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := i.cfg.KubeClient.Create(resourceList); err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If Replace is true, we need to supercede the last release.
|
||||
if i.Replace {
|
||||
if err := i.replaceRelease(rel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Store the release in history before continuing (new in Helm 3). We always know
|
||||
// that this is a create operation.
|
||||
if err := i.cfg.Releases.Create(rel); err != nil {
|
||||
// We could try to recover gracefully here, but since nothing has been installed
|
||||
// yet, this is probably safer than trying to continue when we know storage is
|
||||
// not working.
|
||||
return rel, err
|
||||
}
|
||||
|
||||
// pre-install hooks
|
||||
if !i.DisableHooks {
|
||||
if err := i.cfg.execHook(rel, release.HookPreInstall, i.Timeout); err != nil {
|
||||
return i.failRelease(rel, fmt.Errorf("failed pre-install: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we can do the install. Note that before we were detecting whether to
|
||||
// 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 {
|
||||
return i.failRelease(rel, err)
|
||||
}
|
||||
} else if len(resources) > 0 {
|
||||
if _, err := i.cfg.KubeClient.Update(toBeAdopted, resources, false); err != nil {
|
||||
return i.failRelease(rel, err)
|
||||
}
|
||||
}
|
||||
|
||||
if i.Wait {
|
||||
if i.WaitForJobs {
|
||||
if err := i.cfg.KubeClient.WaitWithJobs(resources, i.Timeout); err != nil {
|
||||
return i.failRelease(rel, err)
|
||||
}
|
||||
} else {
|
||||
if err := i.cfg.KubeClient.Wait(resources, i.Timeout); err != nil {
|
||||
return i.failRelease(rel, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !i.DisableHooks {
|
||||
if err := i.cfg.execHook(rel, release.HookPostInstall, i.Timeout); err != nil {
|
||||
return i.failRelease(rel, fmt.Errorf("failed post-install: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
if len(i.Description) > 0 {
|
||||
rel.SetStatus(release.StatusDeployed, i.Description)
|
||||
} else {
|
||||
rel.SetStatus(release.StatusDeployed, "Install complete")
|
||||
}
|
||||
|
||||
// This is a tricky case. The release has been created, but the result
|
||||
// cannot be recorded. The truest thing to tell the user is that the
|
||||
// release was created. However, the user will not be able to do anything
|
||||
// further with this release.
|
||||
//
|
||||
// One possible strategy would be to do a timed retry to see if we can get
|
||||
// this stored in the future.
|
||||
if err := i.recordRelease(rel); err != nil {
|
||||
i.cfg.Log("failed to record the release: %s", err)
|
||||
}
|
||||
|
||||
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 {
|
||||
i.cfg.Log("Install failed and atomic is set, uninstalling release")
|
||||
uninstall := NewUninstall(i.cfg)
|
||||
uninstall.DisableHooks = i.DisableHooks
|
||||
uninstall.KeepHistory = false
|
||||
uninstall.Timeout = i.Timeout
|
||||
if _, uninstallErr := uninstall.Run(i.ReleaseName); uninstallErr != nil {
|
||||
return rel, errors.Wrapf(uninstallErr, "an error occurred while uninstalling the release. original install error: %s", err)
|
||||
}
|
||||
return rel, errors.Wrapf(err, "release %s failed, and has been uninstalled due to atomic being set", i.ReleaseName)
|
||||
}
|
||||
i.recordRelease(rel) // Ignore the error, since we have another error to deal with.
|
||||
return rel, err
|
||||
}
|
||||
|
||||
// availableName tests whether a name is available
|
||||
//
|
||||
// Roughly, this will return an error if name is
|
||||
//
|
||||
// - empty
|
||||
// - too long
|
||||
// - already in use, and not deleted
|
||||
// - used by a deleted release, and i.Replace is false
|
||||
func (i *Install) availableName() error {
|
||||
start := i.ReleaseName
|
||||
if start == "" {
|
||||
return errors.New("name is required")
|
||||
}
|
||||
|
||||
if len(start) > releaseNameMaxLen {
|
||||
return errors.Errorf("release name %q exceeds max length of %d", start, releaseNameMaxLen)
|
||||
}
|
||||
|
||||
if i.DryRun {
|
||||
return nil
|
||||
}
|
||||
|
||||
h, err := i.cfg.Releases.History(start)
|
||||
if err != nil || len(h) < 1 {
|
||||
return nil
|
||||
}
|
||||
releaseutil.Reverse(h, releaseutil.SortByRevision)
|
||||
rel := h[0]
|
||||
|
||||
if st := rel.Info.Status; i.Replace && (st == release.StatusUninstalled || st == release.StatusFailed) {
|
||||
return nil
|
||||
}
|
||||
return errors.New("cannot re-use a name that is still in use")
|
||||
}
|
||||
|
||||
// createRelease creates a new release object
|
||||
func (i *Install) createRelease(chrt *chart.Chart, rawVals map[string]interface{}) *release.Release {
|
||||
ts := i.cfg.Now()
|
||||
return &release.Release{
|
||||
Name: i.ReleaseName,
|
||||
Namespace: i.Namespace,
|
||||
Chart: chrt,
|
||||
Config: rawVals,
|
||||
Info: &release.Info{
|
||||
FirstDeployed: ts,
|
||||
LastDeployed: ts,
|
||||
Status: release.StatusUnknown,
|
||||
},
|
||||
Version: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// recordRelease with an update operation in case reuse has been set.
|
||||
func (i *Install) recordRelease(r *release.Release) error {
|
||||
// This is a legacy function which has been reduced to a oneliner. Could probably
|
||||
// refactor it out.
|
||||
return i.cfg.Releases.Update(r)
|
||||
}
|
||||
|
||||
// replaceRelease replaces an older release with this one
|
||||
//
|
||||
// This allows us to re-use names by superseding an existing release with a new one
|
||||
func (i *Install) replaceRelease(rel *release.Release) error {
|
||||
hist, err := i.cfg.Releases.History(rel.Name)
|
||||
if err != nil || len(hist) == 0 {
|
||||
// No releases exist for this name, so we can return early
|
||||
return nil
|
||||
}
|
||||
|
||||
releaseutil.Reverse(hist, releaseutil.SortByRevision)
|
||||
last := hist[0]
|
||||
|
||||
// Update version to the next available
|
||||
rel.Version = last.Version + 1
|
||||
|
||||
// Do not change the status of a failed release.
|
||||
if last.Info.Status == release.StatusFailed {
|
||||
return nil
|
||||
}
|
||||
|
||||
// For any other status, mark it as superseded and store the old record
|
||||
last.SetStatus(release.StatusSuperseded, "superseded by new release")
|
||||
return i.recordRelease(last)
|
||||
}
|
||||
|
||||
// write the <data> to <output-dir>/<name>. <append> controls if the file is created or content will be appended
|
||||
func writeToFile(outputDir string, name string, data string, append bool) error {
|
||||
outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator))
|
||||
|
||||
err := ensureDirectoryForFile(outfileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := createOrOpenFile(outfileName, append)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.WriteString(fmt.Sprintf("---\n# Source: %s\n%s\n", name, data))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("wrote %s\n", outfileName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func createOrOpenFile(filename string, append bool) (*os.File, error) {
|
||||
if append {
|
||||
return os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600)
|
||||
}
|
||||
return os.Create(filename)
|
||||
}
|
||||
|
||||
// check if the directory exists to create file. creates if don't exists
|
||||
func ensureDirectoryForFile(file string) error {
|
||||
baseDir := path.Dir(file)
|
||||
_, err := os.Stat(baseDir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.MkdirAll(baseDir, defaultDirectoryPermission)
|
||||
}
|
||||
|
||||
// NameAndChart returns the name and chart that should be used.
|
||||
//
|
||||
// This will read the flags and handle name generation if necessary.
|
||||
func (i *Install) NameAndChart(args []string) (string, string, error) {
|
||||
flagsNotSet := func() error {
|
||||
if i.GenerateName {
|
||||
return errors.New("cannot set --generate-name and also specify a name")
|
||||
}
|
||||
if i.NameTemplate != "" {
|
||||
return errors.New("cannot set --name-template and also specify a name")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(args) > 2 {
|
||||
return args[0], args[1], errors.Errorf("expected at most two arguments, unexpected arguments: %v", strings.Join(args[2:], ", "))
|
||||
}
|
||||
|
||||
if len(args) == 2 {
|
||||
return args[0], args[1], flagsNotSet()
|
||||
}
|
||||
|
||||
if i.NameTemplate != "" {
|
||||
name, err := TemplateName(i.NameTemplate)
|
||||
return name, args[0], err
|
||||
}
|
||||
|
||||
if i.ReleaseName != "" {
|
||||
return i.ReleaseName, args[0], nil
|
||||
}
|
||||
|
||||
if !i.GenerateName {
|
||||
return "", args[0], errors.New("must either provide a name or specify --generate-name")
|
||||
}
|
||||
|
||||
base := filepath.Base(args[0])
|
||||
if base == "." || base == "" {
|
||||
base = "chart"
|
||||
}
|
||||
// if present, strip out the file extension from the name
|
||||
if idx := strings.Index(base, "."); idx != -1 {
|
||||
base = base[0:idx]
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s-%d", base, time.Now().Unix()), args[0], nil
|
||||
}
|
||||
|
||||
// TemplateName renders a name template, returning the name or an error.
|
||||
func TemplateName(nameTemplate string) (string, error) {
|
||||
if nameTemplate == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
t, err := template.New("name-template").Funcs(sprig.TxtFuncMap()).Parse(nameTemplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var b bytes.Buffer
|
||||
if err := t.Execute(&b, nil); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
// CheckDependencies checks the dependencies for a chart.
|
||||
func CheckDependencies(ch *chart.Chart, reqs []*chart.Dependency) error {
|
||||
var missing []string
|
||||
|
||||
OUTER:
|
||||
for _, r := range reqs {
|
||||
for _, d := range ch.Dependencies() {
|
||||
if d.Name() == r.Name {
|
||||
continue OUTER
|
||||
}
|
||||
}
|
||||
missing = append(missing, r.Name)
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
return errors.Errorf("found in Chart.yaml, but missing in charts/ directory: %s", strings.Join(missing, ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LocateChart looks for a chart directory in known places, and returns either the full path or an error.
|
||||
//
|
||||
// This does not ensure that the chart is well-formed; only that the requested filename exists.
|
||||
//
|
||||
// Order of resolution:
|
||||
// - relative to current working directory
|
||||
// - if path is absolute or begins with '.', error out here
|
||||
// - URL
|
||||
//
|
||||
// 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) {
|
||||
name = strings.TrimSpace(name)
|
||||
version := strings.TrimSpace(c.Version)
|
||||
|
||||
if _, err := os.Stat(name); err == nil {
|
||||
abs, err := filepath.Abs(name)
|
||||
if err != nil {
|
||||
return abs, err
|
||||
}
|
||||
if c.Verify {
|
||||
if _, err := downloader.VerifyChart(abs, c.Keyring); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return abs, nil
|
||||
}
|
||||
if filepath.IsAbs(name) || strings.HasPrefix(name, ".") {
|
||||
return name, errors.Errorf("path %q not found", name)
|
||||
}
|
||||
|
||||
dl := downloader.ChartDownloader{
|
||||
Out: os.Stdout,
|
||||
Keyring: c.Keyring,
|
||||
Getters: getter.All(settings),
|
||||
Options: []getter.Option{
|
||||
getter.WithPassCredentialsAll(c.PassCredentialsAll),
|
||||
getter.WithTLSClientConfig(c.CertFile, c.KeyFile, c.CaFile),
|
||||
getter.WithInsecureSkipVerifyTLS(c.InsecureSkipTLSverify),
|
||||
},
|
||||
RepositoryConfig: settings.RepositoryConfig,
|
||||
RepositoryCache: settings.RepositoryCache,
|
||||
}
|
||||
if c.Verify {
|
||||
dl.Verify = downloader.VerifyAlways
|
||||
}
|
||||
if c.RepoURL != "" {
|
||||
chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(c.RepoURL, c.Username, c.Password, name, version,
|
||||
c.CertFile, c.KeyFile, c.CaFile, c.InsecureSkipTLSverify, c.PassCredentialsAll, getter.All(settings))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name = chartURL
|
||||
|
||||
// Only pass the user/pass on when the user has said to or when the
|
||||
// location of the chart repo and the chart are the same domain.
|
||||
u1, err := url.Parse(c.RepoURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
u2, err := url.Parse(chartURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Host on URL (returned from url.Parse) contains the port if present.
|
||||
// This check ensures credentials are not passed between different
|
||||
// services on different ports.
|
||||
if c.PassCredentialsAll || (u1.Scheme == u2.Scheme && u1.Host == u2.Host) {
|
||||
dl.Options = append(dl.Options, getter.WithBasicAuth(c.Username, c.Password))
|
||||
} else {
|
||||
dl.Options = append(dl.Options, getter.WithBasicAuth("", ""))
|
||||
}
|
||||
} else {
|
||||
dl.Options = append(dl.Options, getter.WithBasicAuth(c.Username, c.Password))
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(settings.RepositoryCache, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
filename, _, err := dl.DownloadTo(name, version, settings.RepositoryCache)
|
||||
if err == nil {
|
||||
lname, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return filename, err
|
||||
}
|
||||
return lname, nil
|
||||
} else if settings.Debug {
|
||||
return filename, err
|
||||
}
|
||||
|
||||
atVersion := ""
|
||||
if version != "" {
|
||||
atVersion = fmt.Sprintf(" at version %q", version)
|
||||
}
|
||||
return filename, errors.Errorf("failed to download %q%s (hint: running `helm repo update` may help)", name, atVersion)
|
||||
}
|
||||
197
vendor/helm.sh/helm/v3/pkg/action/lazyclient.go
vendored
Normal file
197
vendor/helm.sh/helm/v3/pkg/action/lazyclient.go
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
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 (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
applycorev1 "k8s.io/client-go/applyconfigurations/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
)
|
||||
|
||||
// lazyClient is a workaround to deal with Kubernetes having an unstable client API.
|
||||
// In Kubernetes v1.18 the defaults where removed which broke creating a
|
||||
// client without an explicit configuration. ಠ_ಠ
|
||||
type lazyClient struct {
|
||||
// client caches an initialized kubernetes client
|
||||
initClient sync.Once
|
||||
client kubernetes.Interface
|
||||
clientErr error
|
||||
|
||||
// clientFn loads a kubernetes client
|
||||
clientFn func() (*kubernetes.Clientset, error)
|
||||
|
||||
// namespace passed to each client request
|
||||
namespace string
|
||||
}
|
||||
|
||||
func (s *lazyClient) init() error {
|
||||
s.initClient.Do(func() {
|
||||
s.client, s.clientErr = s.clientFn()
|
||||
})
|
||||
return s.clientErr
|
||||
}
|
||||
|
||||
// secretClient implements a corev1.SecretsInterface
|
||||
type secretClient struct{ *lazyClient }
|
||||
|
||||
var _ corev1.SecretInterface = (*secretClient)(nil)
|
||||
|
||||
func newSecretClient(lc *lazyClient) *secretClient {
|
||||
return &secretClient{lazyClient: lc}
|
||||
}
|
||||
|
||||
func (s *secretClient) Create(ctx context.Context, secret *v1.Secret, opts metav1.CreateOptions) (result *v1.Secret, err error) {
|
||||
if err := s.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.client.CoreV1().Secrets(s.namespace).Create(ctx, secret, opts)
|
||||
}
|
||||
|
||||
func (s *secretClient) Update(ctx context.Context, secret *v1.Secret, opts metav1.UpdateOptions) (*v1.Secret, error) {
|
||||
if err := s.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.client.CoreV1().Secrets(s.namespace).Update(ctx, secret, opts)
|
||||
}
|
||||
|
||||
func (s *secretClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
|
||||
if err := s.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.client.CoreV1().Secrets(s.namespace).Delete(ctx, name, opts)
|
||||
}
|
||||
|
||||
func (s *secretClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
|
||||
if err := s.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.client.CoreV1().Secrets(s.namespace).DeleteCollection(ctx, opts, listOpts)
|
||||
}
|
||||
|
||||
func (s *secretClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Secret, error) {
|
||||
if err := s.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.client.CoreV1().Secrets(s.namespace).Get(ctx, name, opts)
|
||||
}
|
||||
|
||||
func (s *secretClient) List(ctx context.Context, opts metav1.ListOptions) (*v1.SecretList, error) {
|
||||
if err := s.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.client.CoreV1().Secrets(s.namespace).List(ctx, opts)
|
||||
}
|
||||
|
||||
func (s *secretClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
if err := s.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.client.CoreV1().Secrets(s.namespace).Watch(ctx, opts)
|
||||
}
|
||||
|
||||
func (s *secretClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.Secret, error) {
|
||||
if err := s.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.client.CoreV1().Secrets(s.namespace).Patch(ctx, name, pt, data, opts, subresources...)
|
||||
}
|
||||
|
||||
func (s *secretClient) Apply(ctx context.Context, secretConfiguration *applycorev1.SecretApplyConfiguration, opts metav1.ApplyOptions) (*v1.Secret, error) {
|
||||
if err := s.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.client.CoreV1().Secrets(s.namespace).Apply(ctx, secretConfiguration, opts)
|
||||
}
|
||||
|
||||
// configMapClient implements a corev1.ConfigMapInterface
|
||||
type configMapClient struct{ *lazyClient }
|
||||
|
||||
var _ corev1.ConfigMapInterface = (*configMapClient)(nil)
|
||||
|
||||
func newConfigMapClient(lc *lazyClient) *configMapClient {
|
||||
return &configMapClient{lazyClient: lc}
|
||||
}
|
||||
|
||||
func (c *configMapClient) Create(ctx context.Context, configMap *v1.ConfigMap, opts metav1.CreateOptions) (*v1.ConfigMap, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.CoreV1().ConfigMaps(c.namespace).Create(ctx, configMap, opts)
|
||||
}
|
||||
|
||||
func (c *configMapClient) Update(ctx context.Context, configMap *v1.ConfigMap, opts metav1.UpdateOptions) (*v1.ConfigMap, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.CoreV1().ConfigMaps(c.namespace).Update(ctx, configMap, opts)
|
||||
}
|
||||
|
||||
func (c *configMapClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
|
||||
if err := c.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.client.CoreV1().ConfigMaps(c.namespace).Delete(ctx, name, opts)
|
||||
}
|
||||
|
||||
func (c *configMapClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
|
||||
if err := c.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.client.CoreV1().ConfigMaps(c.namespace).DeleteCollection(ctx, opts, listOpts)
|
||||
}
|
||||
|
||||
func (c *configMapClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.ConfigMap, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.CoreV1().ConfigMaps(c.namespace).Get(ctx, name, opts)
|
||||
}
|
||||
|
||||
func (c *configMapClient) List(ctx context.Context, opts metav1.ListOptions) (*v1.ConfigMapList, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.CoreV1().ConfigMaps(c.namespace).List(ctx, opts)
|
||||
}
|
||||
|
||||
func (c *configMapClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.CoreV1().ConfigMaps(c.namespace).Watch(ctx, opts)
|
||||
}
|
||||
|
||||
func (c *configMapClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.ConfigMap, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.CoreV1().ConfigMaps(c.namespace).Patch(ctx, name, pt, data, opts, subresources...)
|
||||
}
|
||||
|
||||
func (c *configMapClient) Apply(ctx context.Context, configMap *applycorev1.ConfigMapApplyConfiguration, opts metav1.ApplyOptions) (*v1.ConfigMap, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.CoreV1().ConfigMaps(c.namespace).Apply(ctx, configMap, opts)
|
||||
}
|
||||
118
vendor/helm.sh/helm/v3/pkg/action/lint.go
vendored
Normal file
118
vendor/helm.sh/helm/v3/pkg/action/lint.go
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/lint"
|
||||
"helm.sh/helm/v3/pkg/lint/support"
|
||||
)
|
||||
|
||||
// Lint is the action for checking that the semantics of a chart are well-formed.
|
||||
//
|
||||
// It provides the implementation of 'helm lint'.
|
||||
type Lint struct {
|
||||
Strict bool
|
||||
Namespace string
|
||||
WithSubcharts bool
|
||||
}
|
||||
|
||||
// LintResult is the result of Lint
|
||||
type LintResult struct {
|
||||
TotalChartsLinted int
|
||||
Messages []support.Message
|
||||
Errors []error
|
||||
}
|
||||
|
||||
// NewLint creates a new Lint object with the given configuration.
|
||||
func NewLint() *Lint {
|
||||
return &Lint{}
|
||||
}
|
||||
|
||||
// Run executes 'helm Lint' against the given chart.
|
||||
func (l *Lint) Run(paths []string, vals map[string]interface{}) *LintResult {
|
||||
lowestTolerance := support.ErrorSev
|
||||
if l.Strict {
|
||||
lowestTolerance = support.WarningSev
|
||||
}
|
||||
result := &LintResult{}
|
||||
for _, path := range paths {
|
||||
linter, err := lintChart(path, vals, l.Namespace, l.Strict)
|
||||
if err != nil {
|
||||
result.Errors = append(result.Errors, err)
|
||||
continue
|
||||
}
|
||||
|
||||
result.Messages = append(result.Messages, linter.Messages...)
|
||||
result.TotalChartsLinted++
|
||||
for _, msg := range linter.Messages {
|
||||
if msg.Severity >= lowestTolerance {
|
||||
result.Errors = append(result.Errors, msg.Err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func lintChart(path string, vals map[string]interface{}, namespace string, strict bool) (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")
|
||||
if err != nil {
|
||||
return linter, errors.Wrap(err, "unable to create temp dir to extract tarball")
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return linter, errors.Wrap(err, "unable to open tarball")
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err = chartutil.Expand(tempDir, file); err != nil {
|
||||
return linter, errors.Wrap(err, "unable to extract tarball")
|
||||
}
|
||||
|
||||
files, err := ioutil.ReadDir(tempDir)
|
||||
if err != nil {
|
||||
return linter, errors.Wrapf(err, "unable to read temporary output directory %s", tempDir)
|
||||
}
|
||||
if !files[0].IsDir() {
|
||||
return linter, errors.Errorf("unexpected file %s in temporary output directory %s", files[0].Name(), tempDir)
|
||||
}
|
||||
|
||||
chartPath = filepath.Join(tempDir, files[0].Name())
|
||||
} else {
|
||||
chartPath = path
|
||||
}
|
||||
|
||||
// Guard: Error out if this is not a chart.
|
||||
if _, err := os.Stat(filepath.Join(chartPath, "Chart.yaml")); err != nil {
|
||||
return linter, errors.Wrap(err, "unable to check Chart.yaml file in chart")
|
||||
}
|
||||
|
||||
return lint.All(chartPath, vals, namespace, strict), nil
|
||||
}
|
||||
323
vendor/helm.sh/helm/v3/pkg/action/list.go
vendored
Normal file
323
vendor/helm.sh/helm/v3/pkg/action/list.go
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
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 (
|
||||
"path"
|
||||
"regexp"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
)
|
||||
|
||||
// ListStates represents zero or more status codes that a list item may have set
|
||||
//
|
||||
// Because this is used as a bitmask filter, more than one bit can be flipped
|
||||
// in the ListStates.
|
||||
type ListStates uint
|
||||
|
||||
const (
|
||||
// ListDeployed filters on status "deployed"
|
||||
ListDeployed ListStates = 1 << iota
|
||||
// ListUninstalled filters on status "uninstalled"
|
||||
ListUninstalled
|
||||
// ListUninstalling filters on status "uninstalling" (uninstall in progress)
|
||||
ListUninstalling
|
||||
// ListPendingInstall filters on status "pending" (deployment in progress)
|
||||
ListPendingInstall
|
||||
// ListPendingUpgrade filters on status "pending_upgrade" (upgrade in progress)
|
||||
ListPendingUpgrade
|
||||
// ListPendingRollback filters on status "pending_rollback" (rollback in progress)
|
||||
ListPendingRollback
|
||||
// ListSuperseded filters on status "superseded" (historical release version that is no longer deployed)
|
||||
ListSuperseded
|
||||
// ListFailed filters on status "failed" (release version not deployed because of error)
|
||||
ListFailed
|
||||
// ListUnknown filters on an unknown status
|
||||
ListUnknown
|
||||
)
|
||||
|
||||
// FromName takes a state name and returns a ListStates representation.
|
||||
//
|
||||
// Currently, there are only names for individual flipped bits, so the returned
|
||||
// ListStates will only match one of the constants. However, it is possible that
|
||||
// this behavior could change in the future.
|
||||
func (s ListStates) FromName(str string) ListStates {
|
||||
switch str {
|
||||
case "deployed":
|
||||
return ListDeployed
|
||||
case "uninstalled":
|
||||
return ListUninstalled
|
||||
case "superseded":
|
||||
return ListSuperseded
|
||||
case "failed":
|
||||
return ListFailed
|
||||
case "uninstalling":
|
||||
return ListUninstalling
|
||||
case "pending-install":
|
||||
return ListPendingInstall
|
||||
case "pending-upgrade":
|
||||
return ListPendingUpgrade
|
||||
case "pending-rollback":
|
||||
return ListPendingRollback
|
||||
}
|
||||
return ListUnknown
|
||||
}
|
||||
|
||||
// ListAll is a convenience for enabling all list filters
|
||||
const ListAll = ListDeployed | ListUninstalled | ListUninstalling | ListPendingInstall | ListPendingRollback | ListPendingUpgrade | ListSuperseded | ListFailed
|
||||
|
||||
// Sorter is a top-level sort
|
||||
type Sorter uint
|
||||
|
||||
const (
|
||||
// ByNameDesc sorts by descending lexicographic order
|
||||
ByNameDesc Sorter = iota + 1
|
||||
// ByDateAsc sorts by ascending dates (oldest updated release first)
|
||||
ByDateAsc
|
||||
// ByDateDesc sorts by descending dates (latest updated release first)
|
||||
ByDateDesc
|
||||
)
|
||||
|
||||
// List is the action for listing releases.
|
||||
//
|
||||
// It provides, for example, the implementation of 'helm list'.
|
||||
// It returns no more than one revision of every release in one specific, or in
|
||||
// all, namespaces.
|
||||
// To list all the revisions of a specific release, see the History action.
|
||||
type List struct {
|
||||
cfg *Configuration
|
||||
|
||||
// All ignores the limit/offset
|
||||
All bool
|
||||
// AllNamespaces searches across namespaces
|
||||
AllNamespaces bool
|
||||
// Sort indicates the sort to use
|
||||
//
|
||||
// see pkg/releaseutil for several useful sorters
|
||||
Sort Sorter
|
||||
// Overrides the default lexicographic sorting
|
||||
ByDate bool
|
||||
SortReverse bool
|
||||
// StateMask accepts a bitmask of states for items to show.
|
||||
// The default is ListDeployed
|
||||
StateMask ListStates
|
||||
// Limit is the number of items to return per Run()
|
||||
Limit int
|
||||
// Offset is the starting index for the Run() call
|
||||
Offset int
|
||||
// Filter is a filter that is applied to the results
|
||||
Filter string
|
||||
Short bool
|
||||
TimeFormat string
|
||||
Uninstalled bool
|
||||
Superseded bool
|
||||
Uninstalling bool
|
||||
Deployed bool
|
||||
Failed bool
|
||||
Pending bool
|
||||
Selector string
|
||||
}
|
||||
|
||||
// NewList constructs a new *List
|
||||
func NewList(cfg *Configuration) *List {
|
||||
return &List{
|
||||
StateMask: ListDeployed | ListFailed,
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the list command, returning a set of matches.
|
||||
func (l *List) Run() ([]*release.Release, error) {
|
||||
if err := l.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var filter *regexp.Regexp
|
||||
if l.Filter != "" {
|
||||
var err error
|
||||
filter, err = regexp.Compile(l.Filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
results, err := l.cfg.Releases.List(func(rel *release.Release) bool {
|
||||
// Skip anything that doesn't match the filter.
|
||||
if filter != nil && !filter.MatchString(rel.Name) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if results == nil {
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// by definition, superseded releases are never shown if
|
||||
// only the latest releases are returned. so if requested statemask
|
||||
// is _only_ ListSuperseded, skip the latest release filter
|
||||
if l.StateMask != ListSuperseded {
|
||||
results = filterLatestReleases(results)
|
||||
}
|
||||
|
||||
// State mask application must occur after filtering to
|
||||
// latest releases, otherwise outdated entries can be returned
|
||||
results = l.filterStateMask(results)
|
||||
|
||||
// Skip anything that doesn't match the selector
|
||||
selectorObj, err := labels.Parse(l.Selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = l.filterSelector(results, selectorObj)
|
||||
|
||||
// Unfortunately, we have to sort before truncating, which can incur substantial overhead
|
||||
l.sort(results)
|
||||
|
||||
// Guard on offset
|
||||
if l.Offset >= len(results) {
|
||||
return []*release.Release{}, nil
|
||||
}
|
||||
|
||||
// Calculate the limit and offset, and then truncate results if necessary.
|
||||
limit := len(results)
|
||||
if l.Limit > 0 && l.Limit < limit {
|
||||
limit = l.Limit
|
||||
}
|
||||
last := l.Offset + limit
|
||||
if l := len(results); l < last {
|
||||
last = l
|
||||
}
|
||||
results = results[l.Offset:last]
|
||||
|
||||
return results, err
|
||||
}
|
||||
|
||||
// sort is an in-place sort where order is based on the value of a.Sort
|
||||
func (l *List) sort(rels []*release.Release) {
|
||||
if l.SortReverse {
|
||||
l.Sort = ByNameDesc
|
||||
}
|
||||
|
||||
if l.ByDate {
|
||||
l.Sort = ByDateDesc
|
||||
if l.SortReverse {
|
||||
l.Sort = ByDateAsc
|
||||
}
|
||||
}
|
||||
|
||||
switch l.Sort {
|
||||
case ByDateDesc:
|
||||
releaseutil.SortByDate(rels)
|
||||
case ByDateAsc:
|
||||
releaseutil.Reverse(rels, releaseutil.SortByDate)
|
||||
case ByNameDesc:
|
||||
releaseutil.Reverse(rels, releaseutil.SortByName)
|
||||
default:
|
||||
releaseutil.SortByName(rels)
|
||||
}
|
||||
}
|
||||
|
||||
// filterLatestReleases returns a list scrubbed of old releases.
|
||||
func filterLatestReleases(releases []*release.Release) []*release.Release {
|
||||
latestReleases := make(map[string]*release.Release)
|
||||
|
||||
for _, rls := range releases {
|
||||
name, namespace := rls.Name, rls.Namespace
|
||||
key := path.Join(namespace, name)
|
||||
if latestRelease, exists := latestReleases[key]; exists && latestRelease.Version > rls.Version {
|
||||
continue
|
||||
}
|
||||
latestReleases[key] = rls
|
||||
}
|
||||
|
||||
var list = make([]*release.Release, 0, len(latestReleases))
|
||||
for _, rls := range latestReleases {
|
||||
list = append(list, rls)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (l *List) filterStateMask(releases []*release.Release) []*release.Release {
|
||||
desiredStateReleases := make([]*release.Release, 0)
|
||||
|
||||
for _, rls := range releases {
|
||||
currentStatus := l.StateMask.FromName(rls.Info.Status.String())
|
||||
mask := l.StateMask & currentStatus
|
||||
if mask == 0 {
|
||||
continue
|
||||
}
|
||||
desiredStateReleases = append(desiredStateReleases, rls)
|
||||
}
|
||||
|
||||
return desiredStateReleases
|
||||
}
|
||||
|
||||
func (l *List) filterSelector(releases []*release.Release, selector labels.Selector) []*release.Release {
|
||||
desiredStateReleases := make([]*release.Release, 0)
|
||||
|
||||
for _, rls := range releases {
|
||||
if selector.Matches(labels.Set(rls.Labels)) {
|
||||
desiredStateReleases = append(desiredStateReleases, rls)
|
||||
}
|
||||
}
|
||||
|
||||
return desiredStateReleases
|
||||
}
|
||||
|
||||
// SetStateMask calculates the state mask based on parameters.
|
||||
func (l *List) SetStateMask() {
|
||||
if l.All {
|
||||
l.StateMask = ListAll
|
||||
return
|
||||
}
|
||||
|
||||
state := ListStates(0)
|
||||
if l.Deployed {
|
||||
state |= ListDeployed
|
||||
}
|
||||
if l.Uninstalled {
|
||||
state |= ListUninstalled
|
||||
}
|
||||
if l.Uninstalling {
|
||||
state |= ListUninstalling
|
||||
}
|
||||
if l.Pending {
|
||||
state |= ListPendingInstall | ListPendingRollback | ListPendingUpgrade
|
||||
}
|
||||
if l.Failed {
|
||||
state |= ListFailed
|
||||
}
|
||||
if l.Superseded {
|
||||
state |= ListSuperseded
|
||||
}
|
||||
|
||||
// Apply a default
|
||||
if state == 0 {
|
||||
state = ListDeployed | ListFailed
|
||||
}
|
||||
|
||||
l.StateMask = state
|
||||
}
|
||||
182
vendor/helm.sh/helm/v3/pkg/action/package.go
vendored
Normal file
182
vendor/helm.sh/helm/v3/pkg/action/package.go
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
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 (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/term"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/provenance"
|
||||
)
|
||||
|
||||
// Package is the action for packaging a chart.
|
||||
//
|
||||
// It provides the implementation of 'helm package'.
|
||||
type Package struct {
|
||||
Sign bool
|
||||
Key string
|
||||
Keyring string
|
||||
PassphraseFile string
|
||||
Version string
|
||||
AppVersion string
|
||||
Destination string
|
||||
DependencyUpdate bool
|
||||
|
||||
RepositoryConfig string
|
||||
RepositoryCache string
|
||||
}
|
||||
|
||||
// NewPackage creates a new Package object with the given configuration.
|
||||
func NewPackage() *Package {
|
||||
return &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) {
|
||||
ch, err := loader.LoadDir(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If version is set, modify the version.
|
||||
if p.Version != "" {
|
||||
ch.Metadata.Version = p.Version
|
||||
}
|
||||
|
||||
if err := validateVersion(ch.Metadata.Version); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if p.AppVersion != "" {
|
||||
ch.Metadata.AppVersion = p.AppVersion
|
||||
}
|
||||
|
||||
if reqs := ch.Metadata.Dependencies; reqs != nil {
|
||||
if err := CheckDependencies(ch, reqs); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
var dest string
|
||||
if p.Destination == "." {
|
||||
// Save to the current working directory.
|
||||
dest, err = os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
// Otherwise save to set destination
|
||||
dest = p.Destination
|
||||
}
|
||||
|
||||
name, err := chartutil.Save(ch, dest)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to save")
|
||||
}
|
||||
|
||||
if p.Sign {
|
||||
err = p.Clearsign(name)
|
||||
}
|
||||
|
||||
return name, err
|
||||
}
|
||||
|
||||
// validateVersion Verify that version is a Version, and error out if it is not.
|
||||
func validateVersion(ver string) error {
|
||||
if _, err := semver.NewVersion(ver); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clearsign signs a chart
|
||||
func (p *Package) Clearsign(filename string) error {
|
||||
// Load keyring
|
||||
signer, err := provenance.NewFromKeyring(p.Keyring, p.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
passphraseFetcher := promptUser
|
||||
if p.PassphraseFile != "" {
|
||||
passphraseFetcher, err = passphraseFileFetcher(p.PassphraseFile, os.Stdin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := signer.DecryptKey(passphraseFetcher); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sig, err := signer.ClearSign(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filename+".prov", []byte(sig), 0644)
|
||||
}
|
||||
|
||||
// promptUser implements provenance.PassphraseFetcher
|
||||
func promptUser(name string) ([]byte, error) {
|
||||
fmt.Printf("Password for key %q > ", name)
|
||||
// syscall.Stdin is not an int in all environments and needs to be coerced
|
||||
// into one there (e.g., Windows)
|
||||
pw, err := term.ReadPassword(int(syscall.Stdin))
|
||||
fmt.Println()
|
||||
return pw, err
|
||||
}
|
||||
|
||||
func passphraseFileFetcher(passphraseFile string, stdin *os.File) (provenance.PassphraseFetcher, error) {
|
||||
file, err := openPassphraseFile(passphraseFile, stdin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
reader := bufio.NewReader(file)
|
||||
passphrase, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(name string) ([]byte, error) {
|
||||
return passphrase, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func openPassphraseFile(passphraseFile string, stdin *os.File) (*os.File, error) {
|
||||
if passphraseFile == "-" {
|
||||
stat, err := stdin.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if (stat.Mode() & os.ModeNamedPipe) == 0 {
|
||||
return nil, errors.New("specified reading passphrase from stdin, without input on stdin")
|
||||
}
|
||||
return stdin, nil
|
||||
}
|
||||
return os.Open(passphraseFile)
|
||||
}
|
||||
169
vendor/helm.sh/helm/v3/pkg/action/pull.go
vendored
Normal file
169
vendor/helm.sh/helm/v3/pkg/action/pull.go
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/downloader"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
// Pull is the action for checking a given release's information.
|
||||
//
|
||||
// It provides the implementation of 'helm pull'.
|
||||
type Pull struct {
|
||||
ChartPathOptions
|
||||
|
||||
Settings *cli.EnvSettings // TODO: refactor this out of pkg/action
|
||||
|
||||
Devel bool
|
||||
Untar bool
|
||||
VerifyLater bool
|
||||
UntarDir string
|
||||
DestDir string
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
type PullOpt func(*Pull)
|
||||
|
||||
func WithConfig(cfg *Configuration) PullOpt {
|
||||
return func(p *Pull) {
|
||||
p.cfg = cfg
|
||||
}
|
||||
}
|
||||
|
||||
// NewPull creates a new Pull object.
|
||||
func NewPull() *Pull {
|
||||
return NewPullWithOpts()
|
||||
}
|
||||
|
||||
// NewPullWithOpts creates a new pull, with configuration options.
|
||||
func NewPullWithOpts(opts ...PullOpt) *Pull {
|
||||
p := &Pull{}
|
||||
for _, fn := range opts {
|
||||
fn(p)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// Run executes 'helm pull' against the given release.
|
||||
func (p *Pull) Run(chartRef string) (string, error) {
|
||||
var out strings.Builder
|
||||
|
||||
c := downloader.ChartDownloader{
|
||||
Out: &out,
|
||||
Keyring: p.Keyring,
|
||||
Verify: downloader.VerifyNever,
|
||||
Getters: getter.All(p.Settings),
|
||||
Options: []getter.Option{
|
||||
getter.WithBasicAuth(p.Username, p.Password),
|
||||
getter.WithPassCredentialsAll(p.PassCredentialsAll),
|
||||
getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile),
|
||||
getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify),
|
||||
},
|
||||
RepositoryConfig: p.Settings.RepositoryConfig,
|
||||
RepositoryCache: p.Settings.RepositoryCache,
|
||||
}
|
||||
|
||||
if strings.HasPrefix(chartRef, "oci://") {
|
||||
if p.Version == "" {
|
||||
return out.String(), errors.Errorf("--version flag is explicitly required for OCI registries")
|
||||
}
|
||||
|
||||
c.Options = append(c.Options,
|
||||
getter.WithRegistryClient(p.cfg.RegistryClient),
|
||||
getter.WithTagName(p.Version))
|
||||
}
|
||||
|
||||
if p.Verify {
|
||||
c.Verify = downloader.VerifyAlways
|
||||
} else if p.VerifyLater {
|
||||
c.Verify = downloader.VerifyLater
|
||||
}
|
||||
|
||||
// If untar is set, we fetch to a tempdir, then untar and copy after
|
||||
// verification.
|
||||
dest := p.DestDir
|
||||
if p.Untar {
|
||||
var err error
|
||||
dest, err = ioutil.TempDir("", "helm-")
|
||||
if err != nil {
|
||||
return out.String(), errors.Wrap(err, "failed to untar")
|
||||
}
|
||||
defer os.RemoveAll(dest)
|
||||
}
|
||||
|
||||
if p.RepoURL != "" {
|
||||
chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, p.PassCredentialsAll, getter.All(p.Settings))
|
||||
if err != nil {
|
||||
return out.String(), err
|
||||
}
|
||||
chartRef = chartURL
|
||||
}
|
||||
|
||||
saved, v, err := c.DownloadTo(chartRef, p.Version, dest)
|
||||
if err != nil {
|
||||
return out.String(), err
|
||||
}
|
||||
|
||||
if p.Verify {
|
||||
for name := range v.SignedBy.Identities {
|
||||
fmt.Fprintf(&out, "Signed by: %v\n", name)
|
||||
}
|
||||
fmt.Fprintf(&out, "Using Key With Fingerprint: %X\n", v.SignedBy.PrimaryKey.Fingerprint)
|
||||
fmt.Fprintf(&out, "Chart Hash Verified: %s\n", v.FileHash)
|
||||
}
|
||||
|
||||
// After verification, untar the chart into the requested directory.
|
||||
if p.Untar {
|
||||
ud := p.UntarDir
|
||||
if !filepath.IsAbs(ud) {
|
||||
ud = filepath.Join(p.DestDir, ud)
|
||||
}
|
||||
// Let udCheck to check conflict file/dir without replacing ud when untarDir is the current directory(.).
|
||||
udCheck := ud
|
||||
if udCheck == "." {
|
||||
_, udCheck = filepath.Split(chartRef)
|
||||
} else {
|
||||
_, chartName := filepath.Split(chartRef)
|
||||
udCheck = filepath.Join(udCheck, chartName)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(udCheck); err != nil {
|
||||
if err := os.MkdirAll(udCheck, 0755); err != nil {
|
||||
return out.String(), errors.Wrap(err, "failed to untar (mkdir)")
|
||||
}
|
||||
|
||||
} else {
|
||||
return out.String(), errors.Errorf("failed to untar: a file or directory with the name %s already exists", udCheck)
|
||||
}
|
||||
|
||||
return out.String(), chartutil.ExpandFile(ud, saved)
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
38
vendor/helm.sh/helm/v3/pkg/action/registry_login.go
vendored
Normal file
38
vendor/helm.sh/helm/v3/pkg/action/registry_login.go
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// RegistryLogin performs a registry login operation.
|
||||
type RegistryLogin struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewRegistryLogin creates a new RegistryLogin object with the given configuration.
|
||||
func NewRegistryLogin(cfg *Configuration) *RegistryLogin {
|
||||
return &RegistryLogin{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the registry login operation
|
||||
func (a *RegistryLogin) Run(out io.Writer, hostname string, username string, password string, insecure bool) error {
|
||||
return a.cfg.RegistryClient.Login(hostname, username, password, insecure)
|
||||
}
|
||||
38
vendor/helm.sh/helm/v3/pkg/action/registry_logout.go
vendored
Normal file
38
vendor/helm.sh/helm/v3/pkg/action/registry_logout.go
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// RegistryLogout performs a registry login operation.
|
||||
type RegistryLogout struct {
|
||||
cfg *Configuration
|
||||
}
|
||||
|
||||
// NewRegistryLogout creates a new RegistryLogout object with the given configuration.
|
||||
func NewRegistryLogout(cfg *Configuration) *RegistryLogout {
|
||||
return &RegistryLogout{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the registry logout operation
|
||||
func (a *RegistryLogout) Run(out io.Writer, hostname string) error {
|
||||
return a.cfg.RegistryClient.Logout(hostname)
|
||||
}
|
||||
138
vendor/helm.sh/helm/v3/pkg/action/release_testing.go
vendored
Normal file
138
vendor/helm.sh/helm/v3/pkg/action/release_testing.go
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
// ReleaseTesting is the action for testing a release.
|
||||
//
|
||||
// It provides the implementation of 'helm test'.
|
||||
type ReleaseTesting struct {
|
||||
cfg *Configuration
|
||||
Timeout time.Duration
|
||||
// Used for fetching logs from test pods
|
||||
Namespace string
|
||||
Filters map[string][]string
|
||||
}
|
||||
|
||||
// NewReleaseTesting creates a new ReleaseTesting object with the given configuration.
|
||||
func NewReleaseTesting(cfg *Configuration) *ReleaseTesting {
|
||||
return &ReleaseTesting{
|
||||
cfg: cfg,
|
||||
Filters: map[string][]string{},
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes 'helm test' against the given release.
|
||||
func (r *ReleaseTesting) Run(name string) (*release.Release, error) {
|
||||
if err := r.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := chartutil.ValidateReleaseName(name); err != nil {
|
||||
return nil, errors.Errorf("releaseTest: Release name is invalid: %s", name)
|
||||
}
|
||||
|
||||
// finds the non-deleted release with the given name
|
||||
rel, err := r.cfg.Releases.Last(name)
|
||||
if err != nil {
|
||||
return rel, err
|
||||
}
|
||||
|
||||
skippedHooks := []*release.Hook{}
|
||||
executingHooks := []*release.Hook{}
|
||||
if len(r.Filters["!name"]) != 0 {
|
||||
for _, h := range rel.Hooks {
|
||||
if contains(r.Filters["!name"], h.Name) {
|
||||
skippedHooks = append(skippedHooks, h)
|
||||
} else {
|
||||
executingHooks = append(executingHooks, h)
|
||||
}
|
||||
}
|
||||
rel.Hooks = executingHooks
|
||||
}
|
||||
if len(r.Filters["name"]) != 0 {
|
||||
executingHooks = nil
|
||||
for _, h := range rel.Hooks {
|
||||
if contains(r.Filters["name"], h.Name) {
|
||||
executingHooks = append(executingHooks, h)
|
||||
} else {
|
||||
skippedHooks = append(skippedHooks, h)
|
||||
}
|
||||
}
|
||||
rel.Hooks = executingHooks
|
||||
}
|
||||
|
||||
if err := r.cfg.execHook(rel, release.HookTest, r.Timeout); err != nil {
|
||||
rel.Hooks = append(skippedHooks, rel.Hooks...)
|
||||
r.cfg.Releases.Update(rel)
|
||||
return rel, err
|
||||
}
|
||||
|
||||
rel.Hooks = append(skippedHooks, rel.Hooks...)
|
||||
return rel, r.cfg.Releases.Update(rel)
|
||||
}
|
||||
|
||||
// GetPodLogs will write the logs for all test pods in the given release into
|
||||
// the given writer. These can be immediately output to the user or captured for
|
||||
// other uses
|
||||
func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error {
|
||||
client, err := r.cfg.KubernetesClientSet()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to get kubernetes client to fetch pod logs")
|
||||
}
|
||||
|
||||
for _, h := range rel.Hooks {
|
||||
for _, e := range h.Events {
|
||||
if e == release.HookTest {
|
||||
req := client.CoreV1().Pods(r.Namespace).GetLogs(h.Name, &v1.PodLogOptions{})
|
||||
logReader, err := req.Stream(context.Background())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to get pod logs for %s", h.Name)
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "POD LOGS: %s\n", h.Name)
|
||||
_, err = io.Copy(out, logReader)
|
||||
fmt.Fprintln(out)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to write pod logs for %s", h.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func contains(arr []string, value string) bool {
|
||||
for _, item := range arr {
|
||||
if item == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
46
vendor/helm.sh/helm/v3/pkg/action/resource_policy.go
vendored
Normal file
46
vendor/helm.sh/helm/v3/pkg/action/resource_policy.go
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
)
|
||||
|
||||
func filterManifestsToKeep(manifests []releaseutil.Manifest) (keep, remaining []releaseutil.Manifest) {
|
||||
for _, m := range manifests {
|
||||
if m.Head.Metadata == nil || m.Head.Metadata.Annotations == nil || len(m.Head.Metadata.Annotations) == 0 {
|
||||
remaining = append(remaining, m)
|
||||
continue
|
||||
}
|
||||
|
||||
resourcePolicyType, ok := m.Head.Metadata.Annotations[kube.ResourcePolicyAnno]
|
||||
if !ok {
|
||||
remaining = append(remaining, m)
|
||||
continue
|
||||
}
|
||||
|
||||
resourcePolicyType = strings.ToLower(strings.TrimSpace(resourcePolicyType))
|
||||
if resourcePolicyType == kube.KeepPolicy {
|
||||
keep = append(keep, m)
|
||||
}
|
||||
|
||||
}
|
||||
return keep, remaining
|
||||
}
|
||||
241
vendor/helm.sh/helm/v3/pkg/action/rollback.go
vendored
Normal file
241
vendor/helm.sh/helm/v3/pkg/action/rollback.go
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
)
|
||||
|
||||
// Rollback is the action for rolling back to a given release.
|
||||
//
|
||||
// It provides the implementation of 'helm rollback'.
|
||||
type Rollback struct {
|
||||
cfg *Configuration
|
||||
|
||||
Version int
|
||||
Timeout time.Duration
|
||||
Wait bool
|
||||
WaitForJobs bool
|
||||
DisableHooks bool
|
||||
DryRun bool
|
||||
Recreate bool // will (if true) recreate pods after a rollback.
|
||||
Force bool // will (if true) force resource upgrade through uninstall/recreate if needed
|
||||
CleanupOnFail bool
|
||||
MaxHistory int // MaxHistory limits the maximum number of revisions saved per release
|
||||
}
|
||||
|
||||
// NewRollback creates a new Rollback object with the given configuration.
|
||||
func NewRollback(cfg *Configuration) *Rollback {
|
||||
return &Rollback{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes 'helm rollback' against the given release.
|
||||
func (r *Rollback) Run(name string) error {
|
||||
if err := r.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.cfg.Releases.MaxHistory = r.MaxHistory
|
||||
|
||||
r.cfg.Log("preparing rollback of %s", name)
|
||||
currentRelease, targetRelease, err := r.prepareRollback(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.DryRun {
|
||||
r.cfg.Log("creating rolled back release for %s", name)
|
||||
if err := r.cfg.Releases.Create(targetRelease); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r.cfg.Log("performing rollback of %s", name)
|
||||
if _, err := r.performRollback(currentRelease, targetRelease); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.DryRun {
|
||||
r.cfg.Log("updating status for rolled back release for %s", name)
|
||||
if err := r.cfg.Releases.Update(targetRelease); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// prepareRollback finds the previous release and prepares a new release object with
|
||||
// the previous release's configuration
|
||||
func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Release, error) {
|
||||
if err := chartutil.ValidateReleaseName(name); err != nil {
|
||||
return nil, nil, errors.Errorf("prepareRollback: Release name is invalid: %s", name)
|
||||
}
|
||||
|
||||
if r.Version < 0 {
|
||||
return nil, nil, errInvalidRevision
|
||||
}
|
||||
|
||||
currentRelease, err := r.cfg.Releases.Last(name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
previousVersion := r.Version
|
||||
if r.Version == 0 {
|
||||
previousVersion = currentRelease.Version - 1
|
||||
}
|
||||
|
||||
r.cfg.Log("rolling back %s (current: v%d, target: v%d)", name, currentRelease.Version, previousVersion)
|
||||
|
||||
previousRelease, err := r.cfg.Releases.Get(name, previousVersion)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Store a new release object with previous release's configuration
|
||||
targetRelease := &release.Release{
|
||||
Name: name,
|
||||
Namespace: currentRelease.Namespace,
|
||||
Chart: previousRelease.Chart,
|
||||
Config: previousRelease.Config,
|
||||
Info: &release.Info{
|
||||
FirstDeployed: currentRelease.Info.FirstDeployed,
|
||||
LastDeployed: helmtime.Now(),
|
||||
Status: release.StatusPendingRollback,
|
||||
Notes: previousRelease.Info.Notes,
|
||||
// Because we lose the reference to previous version elsewhere, we set the
|
||||
// message here, and only override it later if we experience failure.
|
||||
Description: fmt.Sprintf("Rollback to %d", previousVersion),
|
||||
},
|
||||
Version: currentRelease.Version + 1,
|
||||
Manifest: previousRelease.Manifest,
|
||||
Hooks: previousRelease.Hooks,
|
||||
}
|
||||
|
||||
return currentRelease, targetRelease, nil
|
||||
}
|
||||
|
||||
func (r *Rollback) performRollback(currentRelease, targetRelease *release.Release) (*release.Release, error) {
|
||||
if r.DryRun {
|
||||
r.cfg.Log("dry run for %s", targetRelease.Name)
|
||||
return targetRelease, nil
|
||||
}
|
||||
|
||||
current, err := r.cfg.KubeClient.Build(bytes.NewBufferString(currentRelease.Manifest), false)
|
||||
if err != nil {
|
||||
return targetRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest")
|
||||
}
|
||||
target, err := r.cfg.KubeClient.Build(bytes.NewBufferString(targetRelease.Manifest), false)
|
||||
if err != nil {
|
||||
return targetRelease, errors.Wrap(err, "unable to build kubernetes objects from new release manifest")
|
||||
}
|
||||
|
||||
// pre-rollback hooks
|
||||
if !r.DisableHooks {
|
||||
if err := r.cfg.execHook(targetRelease, release.HookPreRollback, r.Timeout); err != nil {
|
||||
return targetRelease, err
|
||||
}
|
||||
} else {
|
||||
r.cfg.Log("rollback hooks disabled for %s", targetRelease.Name)
|
||||
}
|
||||
|
||||
results, err := r.cfg.KubeClient.Update(current, target, r.Force)
|
||||
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Rollback %q failed: %s", targetRelease.Name, err)
|
||||
r.cfg.Log("warning: %s", msg)
|
||||
currentRelease.Info.Status = release.StatusSuperseded
|
||||
targetRelease.Info.Status = release.StatusFailed
|
||||
targetRelease.Info.Description = msg
|
||||
r.cfg.recordRelease(currentRelease)
|
||||
r.cfg.recordRelease(targetRelease)
|
||||
if r.CleanupOnFail {
|
||||
r.cfg.Log("Cleanup on fail set, cleaning up %d resources", len(results.Created))
|
||||
_, errs := r.cfg.KubeClient.Delete(results.Created)
|
||||
if errs != nil {
|
||||
var errorList []string
|
||||
for _, e := range errs {
|
||||
errorList = append(errorList, e.Error())
|
||||
}
|
||||
return targetRelease, errors.Wrapf(fmt.Errorf("unable to cleanup resources: %s", strings.Join(errorList, ", ")), "an error occurred while cleaning up resources. original rollback error: %s", err)
|
||||
}
|
||||
r.cfg.Log("Resource cleanup complete")
|
||||
}
|
||||
return targetRelease, err
|
||||
}
|
||||
|
||||
if r.Recreate {
|
||||
// NOTE: Because this is not critical for a release to succeed, we just
|
||||
// log if an error occurs and continue onward. If we ever introduce log
|
||||
// levels, we should make these error level logs so users are notified
|
||||
// that they'll need to go do the cleanup on their own
|
||||
if err := recreate(r.cfg, results.Updated); err != nil {
|
||||
r.cfg.Log(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if r.Wait {
|
||||
if r.WaitForJobs {
|
||||
if err := r.cfg.KubeClient.WaitWithJobs(target, r.Timeout); err != nil {
|
||||
targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
|
||||
r.cfg.recordRelease(currentRelease)
|
||||
r.cfg.recordRelease(targetRelease)
|
||||
return targetRelease, errors.Wrapf(err, "release %s failed", targetRelease.Name)
|
||||
}
|
||||
} else {
|
||||
if err := r.cfg.KubeClient.Wait(target, r.Timeout); err != nil {
|
||||
targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
|
||||
r.cfg.recordRelease(currentRelease)
|
||||
r.cfg.recordRelease(targetRelease)
|
||||
return targetRelease, errors.Wrapf(err, "release %s failed", targetRelease.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// post-rollback hooks
|
||||
if !r.DisableHooks {
|
||||
if err := r.cfg.execHook(targetRelease, release.HookPostRollback, r.Timeout); err != nil {
|
||||
return targetRelease, err
|
||||
}
|
||||
}
|
||||
|
||||
deployed, err := r.cfg.Releases.DeployedAll(currentRelease.Name)
|
||||
if err != nil && !strings.Contains(err.Error(), "has no deployed releases") {
|
||||
return nil, err
|
||||
}
|
||||
// Supersede all previous deployments, see issue #2941.
|
||||
for _, rel := range deployed {
|
||||
r.cfg.Log("superseding previous deployment %d", rel.Version)
|
||||
rel.Info.Status = release.StatusSuperseded
|
||||
r.cfg.recordRelease(rel)
|
||||
}
|
||||
|
||||
targetRelease.Info.Status = release.StatusDeployed
|
||||
|
||||
return targetRelease, nil
|
||||
}
|
||||
130
vendor/helm.sh/helm/v3/pkg/action/show.go
vendored
Normal file
130
vendor/helm.sh/helm/v3/pkg/action/show.go
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/cli-runtime/pkg/printers"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
)
|
||||
|
||||
// ShowOutputFormat is the format of the output of `helm show`
|
||||
type ShowOutputFormat string
|
||||
|
||||
const (
|
||||
// ShowAll is the format which shows all the information of a chart
|
||||
ShowAll ShowOutputFormat = "all"
|
||||
// ShowChart is the format which only shows the chart's definition
|
||||
ShowChart ShowOutputFormat = "chart"
|
||||
// ShowValues is the format which only shows the chart's values
|
||||
ShowValues ShowOutputFormat = "values"
|
||||
// ShowReadme is the format which only shows the chart's README
|
||||
ShowReadme ShowOutputFormat = "readme"
|
||||
)
|
||||
|
||||
var readmeFileNames = []string{"readme.md", "readme.txt", "readme"}
|
||||
|
||||
func (o ShowOutputFormat) String() string {
|
||||
return string(o)
|
||||
}
|
||||
|
||||
// Show is the action for checking a given release's information.
|
||||
//
|
||||
// It provides the implementation of 'helm show' and its respective subcommands.
|
||||
type Show struct {
|
||||
ChartPathOptions
|
||||
Devel bool
|
||||
OutputFormat ShowOutputFormat
|
||||
JSONPathTemplate string
|
||||
chart *chart.Chart // for testing
|
||||
}
|
||||
|
||||
// NewShow creates a new Show object with the given configuration.
|
||||
func NewShow(output ShowOutputFormat) *Show {
|
||||
return &Show{
|
||||
OutputFormat: output,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes 'helm show' against the given release.
|
||||
func (s *Show) Run(chartpath string) (string, error) {
|
||||
if s.chart == nil {
|
||||
chrt, err := loader.Load(chartpath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s.chart = chrt
|
||||
}
|
||||
cf, err := yaml.Marshal(s.chart.Metadata)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var out strings.Builder
|
||||
if s.OutputFormat == ShowChart || s.OutputFormat == ShowAll {
|
||||
fmt.Fprintf(&out, "%s\n", cf)
|
||||
}
|
||||
|
||||
if (s.OutputFormat == ShowValues || s.OutputFormat == ShowAll) && s.chart.Values != nil {
|
||||
if s.OutputFormat == ShowAll {
|
||||
fmt.Fprintln(&out, "---")
|
||||
}
|
||||
if s.JSONPathTemplate != "" {
|
||||
printer, err := printers.NewJSONPathPrinter(s.JSONPathTemplate)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error parsing jsonpath %s", s.JSONPathTemplate)
|
||||
}
|
||||
printer.Execute(&out, s.chart.Values)
|
||||
} else {
|
||||
for _, f := range s.chart.Raw {
|
||||
if f.Name == chartutil.ValuesfileName {
|
||||
fmt.Fprintln(&out, string(f.Data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.OutputFormat == ShowReadme || s.OutputFormat == ShowAll {
|
||||
if s.OutputFormat == ShowAll {
|
||||
fmt.Fprintln(&out, "---")
|
||||
}
|
||||
readme := findReadme(s.chart.Files)
|
||||
if readme == nil {
|
||||
return out.String(), nil
|
||||
}
|
||||
fmt.Fprintf(&out, "%s\n", readme.Data)
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
func findReadme(files []*chart.File) (file *chart.File) {
|
||||
for _, file := range files {
|
||||
for _, n := range readmeFileNames {
|
||||
if strings.EqualFold(file.Name, n) {
|
||||
return file
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
51
vendor/helm.sh/helm/v3/pkg/action/status.go
vendored
Normal file
51
vendor/helm.sh/helm/v3/pkg/action/status.go
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 (
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
// Status is the action for checking the deployment status of releases.
|
||||
//
|
||||
// It provides the implementation of 'helm status'.
|
||||
type Status struct {
|
||||
cfg *Configuration
|
||||
|
||||
Version int
|
||||
|
||||
// If true, display description to output format,
|
||||
// only affect print type table.
|
||||
// TODO Helm 4: Remove this flag and output the description by default.
|
||||
ShowDescription bool
|
||||
}
|
||||
|
||||
// NewStatus creates a new Status object with the given configuration.
|
||||
func NewStatus(cfg *Configuration) *Status {
|
||||
return &Status{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes 'helm status' against the given release.
|
||||
func (s *Status) Run(name string) (*release.Release, error) {
|
||||
if err := s.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.cfg.releaseContent(name, s.Version)
|
||||
}
|
||||
212
vendor/helm.sh/helm/v3/pkg/action/uninstall.go
vendored
Normal file
212
vendor/helm.sh/helm/v3/pkg/action/uninstall.go
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
)
|
||||
|
||||
// Uninstall is the action for uninstalling releases.
|
||||
//
|
||||
// It provides the implementation of 'helm uninstall'.
|
||||
type Uninstall struct {
|
||||
cfg *Configuration
|
||||
|
||||
DisableHooks bool
|
||||
DryRun bool
|
||||
KeepHistory bool
|
||||
Timeout time.Duration
|
||||
Description string
|
||||
}
|
||||
|
||||
// NewUninstall creates a new Uninstall object with the given configuration.
|
||||
func NewUninstall(cfg *Configuration) *Uninstall {
|
||||
return &Uninstall{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run uninstalls the given release.
|
||||
func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error) {
|
||||
if err := u.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.DryRun {
|
||||
// In the dry run case, just see if the release exists
|
||||
r, err := u.cfg.releaseContent(name, 0)
|
||||
if err != nil {
|
||||
return &release.UninstallReleaseResponse{}, err
|
||||
}
|
||||
return &release.UninstallReleaseResponse{Release: r}, nil
|
||||
}
|
||||
|
||||
if err := chartutil.ValidateReleaseName(name); err != nil {
|
||||
return nil, errors.Errorf("uninstall: Release name is invalid: %s", name)
|
||||
}
|
||||
|
||||
rels, err := u.cfg.Releases.History(name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "uninstall: Release not loaded: %s", name)
|
||||
}
|
||||
if len(rels) < 1 {
|
||||
return nil, errMissingRelease
|
||||
}
|
||||
|
||||
releaseutil.SortByRevision(rels)
|
||||
rel := rels[len(rels)-1]
|
||||
|
||||
// TODO: Are there any cases where we want to force a delete even if it's
|
||||
// already marked deleted?
|
||||
if rel.Info.Status == release.StatusUninstalled {
|
||||
if !u.KeepHistory {
|
||||
if err := u.purgeReleases(rels...); err != nil {
|
||||
return nil, errors.Wrap(err, "uninstall: Failed to purge the release")
|
||||
}
|
||||
return &release.UninstallReleaseResponse{Release: rel}, nil
|
||||
}
|
||||
return nil, errors.Errorf("the release named %q is already deleted", name)
|
||||
}
|
||||
|
||||
u.cfg.Log("uninstall: Deleting %s", name)
|
||||
rel.Info.Status = release.StatusUninstalling
|
||||
rel.Info.Deleted = helmtime.Now()
|
||||
rel.Info.Description = "Deletion in progress (or silently failed)"
|
||||
res := &release.UninstallReleaseResponse{Release: rel}
|
||||
|
||||
if !u.DisableHooks {
|
||||
if err := u.cfg.execHook(rel, release.HookPreDelete, u.Timeout); err != nil {
|
||||
return res, err
|
||||
}
|
||||
} else {
|
||||
u.cfg.Log("delete hooks disabled for %s", name)
|
||||
}
|
||||
|
||||
// From here on out, the release is currently considered to be in StatusUninstalling
|
||||
// state.
|
||||
if err := u.cfg.Releases.Update(rel); err != nil {
|
||||
u.cfg.Log("uninstall: Failed to store updated release: %s", err)
|
||||
}
|
||||
|
||||
kept, errs := u.deleteRelease(rel)
|
||||
|
||||
if kept != "" {
|
||||
kept = "These resources were kept due to the resource policy:\n" + kept
|
||||
}
|
||||
res.Info = kept
|
||||
|
||||
if !u.DisableHooks {
|
||||
if err := u.cfg.execHook(rel, release.HookPostDelete, u.Timeout); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
rel.Info.Status = release.StatusUninstalled
|
||||
if len(u.Description) > 0 {
|
||||
rel.Info.Description = u.Description
|
||||
} else {
|
||||
rel.Info.Description = "Uninstallation complete"
|
||||
}
|
||||
|
||||
if !u.KeepHistory {
|
||||
u.cfg.Log("purge requested for %s", name)
|
||||
err := u.purgeReleases(rels...)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrap(err, "uninstall: Failed to purge the release"))
|
||||
}
|
||||
|
||||
// Return the errors that occurred while deleting the release, if any
|
||||
if len(errs) > 0 {
|
||||
return res, errors.Errorf("uninstallation completed with %d error(s): %s", len(errs), joinErrors(errs))
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
if err := u.cfg.Releases.Update(rel); err != nil {
|
||||
u.cfg.Log("uninstall: Failed to store updated release: %s", err)
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return res, errors.Errorf("uninstallation completed with %d error(s): %s", len(errs), joinErrors(errs))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (u *Uninstall) purgeReleases(rels ...*release.Release) error {
|
||||
for _, rel := range rels {
|
||||
if _, err := u.cfg.Releases.Delete(rel.Name, rel.Version); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func joinErrors(errs []error) string {
|
||||
es := make([]string, 0, len(errs))
|
||||
for _, e := range errs {
|
||||
es = append(es, e.Error())
|
||||
}
|
||||
return strings.Join(es, "; ")
|
||||
}
|
||||
|
||||
// deleteRelease deletes the release and returns manifests that were kept in the deletion process
|
||||
func (u *Uninstall) deleteRelease(rel *release.Release) (string, []error) {
|
||||
var errs []error
|
||||
caps, err := u.cfg.getCapabilities()
|
||||
if err != nil {
|
||||
return rel.Manifest, []error{errors.Wrap(err, "could not get apiVersions from Kubernetes")}
|
||||
}
|
||||
|
||||
manifests := releaseutil.SplitManifests(rel.Manifest)
|
||||
_, files, err := releaseutil.SortManifests(manifests, caps.APIVersions, releaseutil.UninstallOrder)
|
||||
if err != nil {
|
||||
// We could instead just delete everything in no particular order.
|
||||
// FIXME: One way to delete at this point would be to try a label-based
|
||||
// deletion. The problem with this is that we could get a false positive
|
||||
// and delete something that was not legitimately part of this release.
|
||||
return rel.Manifest, []error{errors.Wrap(err, "corrupted release record. You must manually delete the resources")}
|
||||
}
|
||||
|
||||
filesToKeep, filesToDelete := filterManifestsToKeep(files)
|
||||
var kept string
|
||||
for _, f := range filesToKeep {
|
||||
kept += "[" + f.Head.Kind + "] " + f.Head.Metadata.Name + "\n"
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
for _, file := range filesToDelete {
|
||||
builder.WriteString("\n---\n" + file.Content)
|
||||
}
|
||||
|
||||
resources, err := u.cfg.KubeClient.Build(strings.NewReader(builder.String()), false)
|
||||
if err != nil {
|
||||
return "", []error{errors.Wrap(err, "unable to build kubernetes objects for delete")}
|
||||
}
|
||||
if len(resources) > 0 {
|
||||
_, errs = u.cfg.KubeClient.Delete(resources)
|
||||
}
|
||||
return kept, errs
|
||||
}
|
||||
509
vendor/helm.sh/helm/v3/pkg/action/upgrade.go
vendored
Normal file
509
vendor/helm.sh/helm/v3/pkg/action/upgrade.go
vendored
Normal file
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
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 (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/postrender"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
)
|
||||
|
||||
// Upgrade is the action for upgrading releases.
|
||||
//
|
||||
// It provides the implementation of 'helm upgrade'.
|
||||
type Upgrade struct {
|
||||
cfg *Configuration
|
||||
|
||||
ChartPathOptions
|
||||
|
||||
// Install is a purely informative flag that indicates whether this upgrade was done in "install" mode.
|
||||
//
|
||||
// Applications may use this to determine whether this Upgrade operation was done as part of a
|
||||
// pure upgrade (Upgrade.Install == false) or as part of an install-or-upgrade operation
|
||||
// (Upgrade.Install == true).
|
||||
//
|
||||
// Setting this to `true` will NOT cause `Upgrade` to perform an install if the release does not exist.
|
||||
// That process must be handled by creating an Install action directly. See cmd/upgrade.go for an
|
||||
// example of how this flag is used.
|
||||
Install bool
|
||||
// Devel indicates that the operation is done in devel mode.
|
||||
Devel bool
|
||||
// Namespace is the namespace in which this operation should be performed.
|
||||
Namespace string
|
||||
// SkipCRDs skips installing CRDs when install flag is enabled during upgrade
|
||||
SkipCRDs bool
|
||||
// Timeout is the timeout for this operation
|
||||
Timeout time.Duration
|
||||
// Wait determines whether the wait operation should be performed after the upgrade is requested.
|
||||
Wait bool
|
||||
// WaitForJobs determines whether the wait operation for the Jobs should be performed after the upgrade is requested.
|
||||
WaitForJobs bool
|
||||
// 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
|
||||
// Force will, if set to `true`, ignore certain warnings and perform the upgrade anyway.
|
||||
//
|
||||
// This should be used with caution.
|
||||
Force bool
|
||||
// ResetValues will reset the values to the chart's built-ins rather than merging with existing.
|
||||
ResetValues bool
|
||||
// ReuseValues will re-use the user's last supplied values.
|
||||
ReuseValues bool
|
||||
// Recreate will (if true) recreate pods after a rollback.
|
||||
Recreate bool
|
||||
// MaxHistory limits the maximum number of revisions saved per release
|
||||
MaxHistory int
|
||||
// Atomic, if true, will roll back on failure.
|
||||
Atomic bool
|
||||
// CleanupOnFail will, if true, cause the upgrade to delete newly-created resources on a failed update.
|
||||
CleanupOnFail bool
|
||||
// SubNotes determines whether sub-notes are rendered in the chart.
|
||||
SubNotes bool
|
||||
// Description is the description of this operation
|
||||
Description string
|
||||
// PostRender is an optional post-renderer
|
||||
//
|
||||
// If this is non-nil, then after templates are rendered, they will be sent to the
|
||||
// post renderer before sending to the Kubernetes API server.
|
||||
PostRenderer postrender.PostRenderer
|
||||
// DisableOpenAPIValidation controls whether OpenAPI validation is enforced.
|
||||
DisableOpenAPIValidation bool
|
||||
}
|
||||
|
||||
// NewUpgrade creates a new Upgrade object with the given configuration.
|
||||
func NewUpgrade(cfg *Configuration) *Upgrade {
|
||||
return &Upgrade{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the upgrade on the given release.
|
||||
func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, error) {
|
||||
if err := u.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure if Atomic is set, that wait is set as well. This makes it so
|
||||
// the user doesn't have to specify both
|
||||
u.Wait = u.Wait || u.Atomic
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.cfg.Releases.MaxHistory = u.MaxHistory
|
||||
|
||||
u.cfg.Log("performing update for %s", name)
|
||||
res, err := u.performUpgrade(currentRelease, upgradedRelease)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if !u.DryRun {
|
||||
u.cfg.Log("updating status for upgraded release for %s", name)
|
||||
if err := u.cfg.Releases.Update(upgradedRelease); err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, nil, errMissingChart
|
||||
}
|
||||
|
||||
// finds the last non-deleted release with the given name
|
||||
lastRelease, err := u.cfg.Releases.Last(name)
|
||||
if err != nil {
|
||||
// to keep existing behavior of returning the "%q has no deployed releases" error when an existing release does not exist
|
||||
if errors.Is(err, driver.ErrReleaseNotFound) {
|
||||
return nil, nil, driver.NewErrNoDeployedReleases(name)
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Concurrent `helm upgrade`s will either fail here with `errPending` or when creating the release with "already exists". This should act as a pessimistic lock.
|
||||
if lastRelease.Info.Status.IsPending() {
|
||||
return nil, nil, errPending
|
||||
}
|
||||
|
||||
var currentRelease *release.Release
|
||||
if lastRelease.Info.Status == release.StatusDeployed {
|
||||
// no need to retrieve the last deployed release from storage as the last release is deployed
|
||||
currentRelease = lastRelease
|
||||
} else {
|
||||
// finds the deployed release with the given name
|
||||
currentRelease, err = u.cfg.Releases.Deployed(name)
|
||||
if err != nil {
|
||||
if errors.Is(err, driver.ErrNoDeployedReleases) &&
|
||||
(lastRelease.Info.Status == release.StatusFailed || lastRelease.Info.Status == release.StatusSuperseded) {
|
||||
currentRelease = lastRelease
|
||||
} else {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// determine if values will be reused
|
||||
vals, err = u.reuseValues(chart, currentRelease, vals)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := chartutil.ProcessDependencies(chart, vals); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Increment revision count. This is passed to templates, and also stored on
|
||||
// the release object.
|
||||
revision := lastRelease.Version + 1
|
||||
|
||||
options := chartutil.ReleaseOptions{
|
||||
Name: name,
|
||||
Namespace: currentRelease.Namespace,
|
||||
Revision: revision,
|
||||
IsUpgrade: true,
|
||||
}
|
||||
|
||||
caps, err := u.cfg.getCapabilities()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
valuesToRender, err := chartutil.ToRenderValues(chart, vals, options, caps)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Store an upgraded release.
|
||||
upgradedRelease := &release.Release{
|
||||
Name: name,
|
||||
Namespace: currentRelease.Namespace,
|
||||
Chart: chart,
|
||||
Config: vals,
|
||||
Info: &release.Info{
|
||||
FirstDeployed: currentRelease.Info.FirstDeployed,
|
||||
LastDeployed: Timestamper(),
|
||||
Status: release.StatusPendingUpgrade,
|
||||
Description: "Preparing upgrade", // This should be overwritten later.
|
||||
},
|
||||
Version: revision,
|
||||
Manifest: manifestDoc.String(),
|
||||
Hooks: hooks,
|
||||
}
|
||||
|
||||
if len(notesTxt) > 0 {
|
||||
upgradedRelease.Info.Notes = notesTxt
|
||||
}
|
||||
err = validateManifest(u.cfg.KubeClient, manifestDoc.Bytes(), !u.DisableOpenAPIValidation)
|
||||
return currentRelease, upgradedRelease, err
|
||||
}
|
||||
|
||||
func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Release) (*release.Release, error) {
|
||||
current, err := u.cfg.KubeClient.Build(bytes.NewBufferString(originalRelease.Manifest), false)
|
||||
if err != nil {
|
||||
// Checking for removed Kubernetes API error so can provide a more informative error message to the user
|
||||
// Ref: https://github.com/helm/helm/issues/7219
|
||||
if strings.Contains(err.Error(), "unable to recognize \"\": no matches for kind") {
|
||||
return upgradedRelease, errors.Wrap(err, "current release manifest contains removed kubernetes api(s) for this "+
|
||||
"kubernetes version and it is therefore unable to build the kubernetes "+
|
||||
"objects for performing the diff. error from kubernetes")
|
||||
}
|
||||
return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest")
|
||||
}
|
||||
target, err := u.cfg.KubeClient.Build(bytes.NewBufferString(upgradedRelease.Manifest), !u.DisableOpenAPIValidation)
|
||||
if err != nil {
|
||||
return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from new release manifest")
|
||||
}
|
||||
|
||||
// It is safe to use force only on target because these are resources currently rendered by the chart.
|
||||
err = target.Visit(setMetadataVisitor(upgradedRelease.Name, upgradedRelease.Namespace, true))
|
||||
if err != nil {
|
||||
return upgradedRelease, err
|
||||
}
|
||||
|
||||
// Do a basic diff using gvk + name to figure out what new resources are being created so we can validate they don't already exist
|
||||
existingResources := make(map[string]bool)
|
||||
for _, r := range current {
|
||||
existingResources[objectKey(r)] = true
|
||||
}
|
||||
|
||||
var toBeCreated kube.ResourceList
|
||||
for _, r := range target {
|
||||
if !existingResources[objectKey(r)] {
|
||||
toBeCreated = append(toBeCreated, r)
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
toBeUpdated.Visit(func(r *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
current.Append(r)
|
||||
return nil
|
||||
})
|
||||
|
||||
if u.DryRun {
|
||||
u.cfg.Log("dry run for %s", upgradedRelease.Name)
|
||||
if len(u.Description) > 0 {
|
||||
upgradedRelease.Info.Description = u.Description
|
||||
} else {
|
||||
upgradedRelease.Info.Description = "Dry run complete"
|
||||
}
|
||||
return upgradedRelease, nil
|
||||
}
|
||||
|
||||
u.cfg.Log("creating upgraded release for %s", upgradedRelease.Name)
|
||||
if err := u.cfg.Releases.Create(upgradedRelease); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// pre-upgrade hooks
|
||||
if !u.DisableHooks {
|
||||
if err := u.cfg.execHook(upgradedRelease, release.HookPreUpgrade, u.Timeout); err != nil {
|
||||
return u.failRelease(upgradedRelease, kube.ResourceList{}, fmt.Errorf("pre-upgrade hooks failed: %s", err))
|
||||
}
|
||||
} else {
|
||||
u.cfg.Log("upgrade hooks disabled for %s", upgradedRelease.Name)
|
||||
}
|
||||
|
||||
results, err := u.cfg.KubeClient.Update(current, target, u.Force)
|
||||
if err != nil {
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
return u.failRelease(upgradedRelease, results.Created, err)
|
||||
}
|
||||
|
||||
if u.Recreate {
|
||||
// NOTE: Because this is not critical for a release to succeed, we just
|
||||
// log if an error occurs and continue onward. If we ever introduce log
|
||||
// levels, we should make these error level logs so users are notified
|
||||
// that they'll need to go do the cleanup on their own
|
||||
if err := recreate(u.cfg, results.Updated); err != nil {
|
||||
u.cfg.Log(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if u.Wait {
|
||||
if u.WaitForJobs {
|
||||
if err := u.cfg.KubeClient.WaitWithJobs(target, u.Timeout); err != nil {
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
return u.failRelease(upgradedRelease, results.Created, err)
|
||||
}
|
||||
} else {
|
||||
if err := u.cfg.KubeClient.Wait(target, u.Timeout); err != nil {
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
return u.failRelease(upgradedRelease, results.Created, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// post-upgrade hooks
|
||||
if !u.DisableHooks {
|
||||
if err := u.cfg.execHook(upgradedRelease, release.HookPostUpgrade, u.Timeout); err != nil {
|
||||
return u.failRelease(upgradedRelease, results.Created, fmt.Errorf("post-upgrade hooks failed: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
originalRelease.Info.Status = release.StatusSuperseded
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
|
||||
upgradedRelease.Info.Status = release.StatusDeployed
|
||||
if len(u.Description) > 0 {
|
||||
upgradedRelease.Info.Description = u.Description
|
||||
} else {
|
||||
upgradedRelease.Info.Description = "Upgrade complete"
|
||||
}
|
||||
|
||||
return upgradedRelease, nil
|
||||
}
|
||||
|
||||
func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, err error) (*release.Release, error) {
|
||||
msg := fmt.Sprintf("Upgrade %q failed: %s", rel.Name, err)
|
||||
u.cfg.Log("warning: %s", msg)
|
||||
|
||||
rel.Info.Status = release.StatusFailed
|
||||
rel.Info.Description = msg
|
||||
u.cfg.recordRelease(rel)
|
||||
if u.CleanupOnFail && len(created) > 0 {
|
||||
u.cfg.Log("Cleanup on fail set, cleaning up %d resources", len(created))
|
||||
_, errs := u.cfg.KubeClient.Delete(created)
|
||||
if errs != nil {
|
||||
var errorList []string
|
||||
for _, e := range errs {
|
||||
errorList = append(errorList, e.Error())
|
||||
}
|
||||
return rel, errors.Wrapf(fmt.Errorf("unable to cleanup resources: %s", strings.Join(errorList, ", ")), "an error occurred while cleaning up resources. original upgrade error: %s", err)
|
||||
}
|
||||
u.cfg.Log("Resource cleanup complete")
|
||||
}
|
||||
if u.Atomic {
|
||||
u.cfg.Log("Upgrade failed and atomic is set, rolling back to last successful release")
|
||||
|
||||
// As a protection, get the last successful release before rollback.
|
||||
// If there are no successful releases, bail out
|
||||
hist := NewHistory(u.cfg)
|
||||
fullHistory, herr := hist.Run(rel.Name)
|
||||
if herr != nil {
|
||||
return rel, errors.Wrapf(herr, "an error occurred while finding last successful release. original upgrade error: %s", err)
|
||||
}
|
||||
|
||||
// There isn't a way to tell if a previous release was successful, but
|
||||
// generally failed releases do not get superseded unless the next
|
||||
// release is successful, so this should be relatively safe
|
||||
filteredHistory := releaseutil.FilterFunc(func(r *release.Release) bool {
|
||||
return r.Info.Status == release.StatusSuperseded || r.Info.Status == release.StatusDeployed
|
||||
}).Filter(fullHistory)
|
||||
if len(filteredHistory) == 0 {
|
||||
return rel, errors.Wrap(err, "unable to find a previously successful release when attempting to rollback. original upgrade error")
|
||||
}
|
||||
|
||||
releaseutil.Reverse(filteredHistory, releaseutil.SortByRevision)
|
||||
|
||||
rollin := NewRollback(u.cfg)
|
||||
rollin.Version = filteredHistory[0].Version
|
||||
rollin.Wait = true
|
||||
rollin.WaitForJobs = u.WaitForJobs
|
||||
rollin.DisableHooks = u.DisableHooks
|
||||
rollin.Recreate = u.Recreate
|
||||
rollin.Force = u.Force
|
||||
rollin.Timeout = u.Timeout
|
||||
if rollErr := rollin.Run(rel.Name); rollErr != nil {
|
||||
return rel, errors.Wrapf(rollErr, "an error occurred while rolling back the release. original upgrade error: %s", err)
|
||||
}
|
||||
return rel, errors.Wrapf(err, "release %s failed, and has been rolled back due to atomic being set", rel.Name)
|
||||
}
|
||||
|
||||
return rel, err
|
||||
}
|
||||
|
||||
// reuseValues copies values from the current release to a new release if the
|
||||
// new release does not have any values.
|
||||
//
|
||||
// If the request already has values, or if there are no values in the current
|
||||
// release, this does nothing.
|
||||
//
|
||||
// This is skipped if the u.ResetValues flag is set, in which case the
|
||||
// request values are not altered.
|
||||
func (u *Upgrade) reuseValues(chart *chart.Chart, current *release.Release, newVals map[string]interface{}) (map[string]interface{}, error) {
|
||||
if u.ResetValues {
|
||||
// If ResetValues is set, we completely ignore current.Config.
|
||||
u.cfg.Log("resetting values to the chart's original version")
|
||||
return newVals, nil
|
||||
}
|
||||
|
||||
// If the ReuseValues flag is set, we always copy the old values over the new config's values.
|
||||
if u.ReuseValues {
|
||||
u.cfg.Log("reusing the old release's values")
|
||||
|
||||
// We have to regenerate the old coalesced values:
|
||||
oldVals, err := chartutil.CoalesceValues(current.Chart, current.Config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to rebuild old values")
|
||||
}
|
||||
|
||||
newVals = chartutil.CoalesceTables(newVals, current.Config)
|
||||
|
||||
chart.Values = oldVals
|
||||
|
||||
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
|
||||
}
|
||||
return newVals, nil
|
||||
}
|
||||
|
||||
func validateManifest(c kube.Interface, manifest []byte, openAPIValidation bool) error {
|
||||
_, err := c.Build(bytes.NewReader(manifest), openAPIValidation)
|
||||
return err
|
||||
}
|
||||
|
||||
// recreate captures all the logic for recreating pods for both upgrade and
|
||||
// rollback. If we end up refactoring rollback to use upgrade, this can just be
|
||||
// made an unexported method on the upgrade action.
|
||||
func recreate(cfg *Configuration, resources kube.ResourceList) error {
|
||||
for _, res := range resources {
|
||||
versioned := kube.AsVersioned(res)
|
||||
selector, err := kube.SelectorsForObject(versioned)
|
||||
if err != nil {
|
||||
// If no selector is returned, it means this object is
|
||||
// definitely not a pod, so continue onward
|
||||
continue
|
||||
}
|
||||
|
||||
client, err := cfg.KubernetesClientSet()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name)
|
||||
}
|
||||
|
||||
pods, err := client.CoreV1().Pods(res.Namespace).List(context.Background(), metav1.ListOptions{
|
||||
LabelSelector: selector.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name)
|
||||
}
|
||||
|
||||
// Restart pods
|
||||
for _, pod := range pods.Items {
|
||||
// Delete each pod for get them restarted with changed spec.
|
||||
if err := client.CoreV1().Pods(pod.Namespace).Delete(context.Background(), pod.Name, *metav1.NewPreconditionDeleteOptions(string(pod.UID))); err != nil {
|
||||
return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
184
vendor/helm.sh/helm/v3/pkg/action/validate.go
vendored
Normal file
184
vendor/helm.sh/helm/v3/pkg/action/validate.go
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
)
|
||||
|
||||
var accessor = meta.NewAccessor()
|
||||
|
||||
const (
|
||||
appManagedByLabel = "app.kubernetes.io/managed-by"
|
||||
appManagedByHelm = "Helm"
|
||||
helmReleaseNameAnnotation = "meta.helm.sh/release-name"
|
||||
helmReleaseNamespaceAnnotation = "meta.helm.sh/release-namespace"
|
||||
)
|
||||
|
||||
func existingResourceConflict(resources kube.ResourceList, releaseName, releaseNamespace string) (kube.ResourceList, error) {
|
||||
var requireUpdate kube.ResourceList
|
||||
|
||||
err := resources.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
helper := resource.NewHelper(info.Client, info.Mapping)
|
||||
existing, err := helper.Get(info.Namespace, info.Name)
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrap(err, "could not get information about the resource")
|
||||
}
|
||||
|
||||
// Allow adoption of the resource if it is managed by Helm and is annotated with correct release name and namespace.
|
||||
if err := checkOwnership(existing, releaseName, releaseNamespace); err != nil {
|
||||
return fmt.Errorf("%s exists and cannot be imported into the current release: %s", resourceString(info), err)
|
||||
}
|
||||
|
||||
requireUpdate.Append(info)
|
||||
return nil
|
||||
})
|
||||
|
||||
return requireUpdate, err
|
||||
}
|
||||
|
||||
func checkOwnership(obj runtime.Object, releaseName, releaseNamespace string) error {
|
||||
lbls, err := accessor.Labels(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
annos, err := accessor.Annotations(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var errs []error
|
||||
if err := requireValue(lbls, appManagedByLabel, appManagedByHelm); err != nil {
|
||||
errs = append(errs, fmt.Errorf("label validation error: %s", err))
|
||||
}
|
||||
if err := requireValue(annos, helmReleaseNameAnnotation, releaseName); err != nil {
|
||||
errs = append(errs, fmt.Errorf("annotation validation error: %s", err))
|
||||
}
|
||||
if err := requireValue(annos, helmReleaseNamespaceAnnotation, releaseNamespace); err != nil {
|
||||
errs = append(errs, fmt.Errorf("annotation validation error: %s", err))
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
err := errors.New("invalid ownership metadata")
|
||||
for _, e := range errs {
|
||||
err = fmt.Errorf("%w; %s", err, e)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func requireValue(meta map[string]string, k, v string) error {
|
||||
actual, ok := meta[k]
|
||||
if !ok {
|
||||
return fmt.Errorf("missing key %q: must be set to %q", k, v)
|
||||
}
|
||||
if actual != v {
|
||||
return fmt.Errorf("key %q must equal %q: current value is %q", k, v, actual)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setMetadataVisitor adds release tracking metadata to all resources. If force is enabled, existing
|
||||
// ownership metadata will be overwritten. Otherwise an error will be returned if any resource has an
|
||||
// existing and conflicting value for the managed by label or Helm release/namespace annotations.
|
||||
func setMetadataVisitor(releaseName, releaseNamespace string, force bool) resource.VisitorFunc {
|
||||
return func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !force {
|
||||
if err := checkOwnership(info.Object, releaseName, releaseNamespace); err != nil {
|
||||
return fmt.Errorf("%s cannot be owned: %s", resourceString(info), err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := mergeLabels(info.Object, map[string]string{
|
||||
appManagedByLabel: appManagedByHelm,
|
||||
}); err != nil {
|
||||
return fmt.Errorf(
|
||||
"%s labels could not be updated: %s",
|
||||
resourceString(info), err,
|
||||
)
|
||||
}
|
||||
|
||||
if err := mergeAnnotations(info.Object, map[string]string{
|
||||
helmReleaseNameAnnotation: releaseName,
|
||||
helmReleaseNamespaceAnnotation: releaseNamespace,
|
||||
}); err != nil {
|
||||
return fmt.Errorf(
|
||||
"%s annotations could not be updated: %s",
|
||||
resourceString(info), err,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func resourceString(info *resource.Info) string {
|
||||
_, k := info.Mapping.GroupVersionKind.ToAPIVersionAndKind()
|
||||
return fmt.Sprintf(
|
||||
"%s %q in namespace %q",
|
||||
k, info.Name, info.Namespace,
|
||||
)
|
||||
}
|
||||
|
||||
func mergeLabels(obj runtime.Object, labels map[string]string) error {
|
||||
current, err := accessor.Labels(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return accessor.SetLabels(obj, mergeStrStrMaps(current, labels))
|
||||
}
|
||||
|
||||
func mergeAnnotations(obj runtime.Object, annotations map[string]string) error {
|
||||
current, err := accessor.Annotations(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return accessor.SetAnnotations(obj, mergeStrStrMaps(current, annotations))
|
||||
}
|
||||
|
||||
// merge two maps, always taking the value on the right
|
||||
func mergeStrStrMaps(current, desired map[string]string) map[string]string {
|
||||
result := make(map[string]string)
|
||||
for k, v := range current {
|
||||
result[k] = v
|
||||
}
|
||||
for k, desiredVal := range desired {
|
||||
result[k] = desiredVal
|
||||
}
|
||||
return result
|
||||
}
|
||||
59
vendor/helm.sh/helm/v3/pkg/action/verify.go
vendored
Normal file
59
vendor/helm.sh/helm/v3/pkg/action/verify.go
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/pkg/downloader"
|
||||
)
|
||||
|
||||
// Verify is the action for building a given chart's Verify tree.
|
||||
//
|
||||
// It provides the implementation of 'helm verify'.
|
||||
type Verify struct {
|
||||
Keyring string
|
||||
Out string
|
||||
}
|
||||
|
||||
// NewVerify creates a new Verify object with the given configuration.
|
||||
func NewVerify() *Verify {
|
||||
return &Verify{}
|
||||
}
|
||||
|
||||
// Run executes 'helm verify'.
|
||||
func (v *Verify) Run(chartfile string) error {
|
||||
var out strings.Builder
|
||||
p, err := downloader.VerifyChart(chartfile, v.Keyring)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for name := range p.SignedBy.Identities {
|
||||
fmt.Fprintf(&out, "Signed by: %v\n", name)
|
||||
}
|
||||
fmt.Fprintf(&out, "Using Key With Fingerprint: %X\n", p.SignedBy.PrimaryKey.Fingerprint)
|
||||
fmt.Fprintf(&out, "Chart Hash Verified: %s\n", p.FileHash)
|
||||
|
||||
// TODO(mattfarina): The output is set as a property rather than returned
|
||||
// to maintain the Go API. In Helm v4 this function should return the out
|
||||
// and the property on the struct can be removed.
|
||||
v.Out = out.String()
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user