Merge branch 'main' into oci-install-digest

pull/12690/head
Matt Farina 10 months ago committed by GitHub
commit 4dd2a7d626
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,7 +1,24 @@
version: 2
updates:
- # Keep dev-v3 branch dependencies up to date, while Helm v3 is within support
package-ecosystem: "gomod"
target_branch: "dev-v3"
directory: "/"
schedule:
interval: "daily"
groups:
k8s.io:
patterns:
- "k8s.io/api"
- "k8s.io/apiextensions-apiserver"
- "k8s.io/apimachinery"
- "k8s.io/apiserver"
- "k8s.io/cli-runtime"
- "k8s.io/client-go"
- "k8s.io/kubectl"
- package-ecosystem: "gomod"
target_branch: "main"
directory: "/"
schedule:
interval: "daily"
@ -16,6 +33,7 @@ updates:
- "k8s.io/client-go"
- "k8s.io/kubectl"
- package-ecosystem: "github-actions"
target_branch: "main"
directory: "/"
schedule:
interval: "daily"

@ -3,6 +3,7 @@ on:
push:
branches:
- "main"
- "dev-v3"
- "release-**"
pull_request:
branches:
@ -16,9 +17,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Setup Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # pin@5.0.2
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # pin@5.1.0
with:
go-version: '1.22'
check-latest: true

@ -13,10 +13,14 @@ name: "CodeQL"
on:
push:
branches: [ main ]
branches:
- main
- dev-v3
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
branches:
- main
- dev-v3
schedule:
- cron: '29 6 * * 6'
@ -39,7 +43,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

@ -13,10 +13,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Setup Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # pin@5.0.2
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # pin@5.1.0
with:
go-version: '1.22'
check-latest: true

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Setup Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # pin@5.0.2
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # pin@5.1.0
with:
go-version: '1.22'
check-latest: true

@ -17,15 +17,15 @@ permissions: read-all
jobs:
release:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
runs-on: ubuntu-latest-16-cores
steps:
- name: Checkout source code
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # pin@5.0.2
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # pin@5.1.0
with:
go-version: '1.22.7'
@ -74,14 +74,14 @@ jobs:
connection_string: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}
canary-release:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-16-cores
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout source code
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Setup Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # pin@5.0.2
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # pin@5.1.0
with:
go-version: '1.22'
check-latest: true

@ -28,7 +28,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
@ -55,7 +55,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20
uses: actions/upload-artifact@c24449f33cd45d4826c6702db7e49f7cdb9b551d # v3.2.1-node20
with:
name: SARIF file
path: results.sarif

@ -9,7 +9,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@87c2b794b9b47a9bec68ae03c01aeb572ffebdb1 # v3.0.14
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue has been marked as stale because it has been open for 90 days with no activity. This thread will be automatically closed in 30 days if no further activity occurs.'

1
.gitignore vendored

@ -12,3 +12,4 @@ bin/
vendor/
# Ignores charts pulled for dependency build tests
cmd/helm/testdata/testcharts/issue-7233/charts/*
.pre-commit-config.yaml

@ -6,12 +6,15 @@
# Organizations Using Helm
- [IBM](https://www.ibm.com)
- [InfoCert](https://www.infocert.it/)
- [Microsoft](https://microsoft.com)
- [Omnistrate](https://omnistrate.com)
- [Octopus Deploy](https://octopus.com/)
- [New Relic](https://www.newrelic.com)
- [Qovery](https://www.qovery.com/)
- [Samsung SDS](https://www.samsungsds.com/)
- [Softonic](https://hello.softonic.com/)
- [SyncTune](https://mb-consulting.dev)
- [Syself](https://syself.com)
- [Ville de Montreal](https://montreal.ca)
- [Intercept](https://Intercept.cloud)

@ -11,6 +11,10 @@ vulnerability_, please email a report to
[cncf-helm-security@lists.cncf.io](mailto:cncf-helm-security@lists.cncf.io). This will give us a
chance to try to fix the issue before it is exploited in the wild.
## Helm v3 and v4
Helm v4 is currently under development on the `main` branch. During the development of Helm v4 and for some time after its released, Helm v3 will continue to be supported and developed on the `dev-v3` branch. Helm v3 will continue to get bug fixes and updates for new Kubernetes releases. Helm v4 is where new features and major changes will happen. For features to be backported to Helm v3, an exception will be needed. Bugs should first be fixed on Helm v4 and then backported to Helm v3.
## Sign Your Work
The sign-off is a simple line at the end of the explanation for a commit. All commits need to be

@ -56,6 +56,8 @@ Get started with the [Quick Start guide](https://helm.sh/docs/intro/quickstart/)
The [Helm roadmap uses GitHub milestones](https://github.com/helm/helm/milestones) to track the progress of the project.
The development of Helm v4 is currently happening on the `main` branch while the development of Helm v3, the stable branch, is happening on the `dev-v3` branch. Changes should be made to the `main` branch prior to being added to the `dev-v3` branch so that all changes are carried along to Helm v4.
## Community, discussion, contribution, and support
You can reach the Helm community and developers via the following channels:

@ -20,6 +20,7 @@ import (
"path/filepath"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
@ -93,7 +94,7 @@ func newDependencyCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
cmd.AddCommand(newDependencyListCmd(out))
cmd.AddCommand(newDependencyUpdateCmd(cfg, out))
cmd.AddCommand(newDependencyBuildCmd(cfg, out))
cmd.AddCommand(newDependencyBuildCmd(out))
return cmd
}
@ -120,3 +121,16 @@ func newDependencyListCmd(out io.Writer) *cobra.Command {
f.UintVar(&client.ColumnWidth, "max-col-width", 80, "maximum column width for output table")
return cmd
}
func addDependencySubcommandFlags(f *pflag.FlagSet, client *action.Dependency) {
f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures")
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.BoolVar(&client.SkipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
f.StringVar(&client.Username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&client.Password, "password", "", "chart repository password where to locate the requested chart")
f.StringVar(&client.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&client.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.BoolVar(&client.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download")
f.BoolVar(&client.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download")
f.StringVar(&client.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
}

@ -41,7 +41,7 @@ If no lock file is found, 'helm dependency build' will mirror the behavior
of 'helm dependency update'.
`
func newDependencyBuildCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
func newDependencyBuildCmd(out io.Writer) *cobra.Command {
client := action.NewDependency()
cmd := &cobra.Command{
@ -54,13 +54,19 @@ func newDependencyBuildCmd(cfg *action.Configuration, out io.Writer) *cobra.Comm
if len(args) > 0 {
chartpath = filepath.Clean(args[0])
}
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
man := &downloader.Manager{
Out: out,
ChartPath: chartpath,
Keyring: client.Keyring,
SkipUpdate: client.SkipRefresh,
Getters: getter.All(settings),
RegistryClient: cfg.RegistryClient,
RegistryClient: registryClient,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
@ -68,7 +74,7 @@ func newDependencyBuildCmd(cfg *action.Configuration, out io.Writer) *cobra.Comm
if client.Verify {
man.Verify = downloader.VerifyIfPossible
}
err := man.Build()
err = man.Build()
if e, ok := err.(downloader.ErrRepoNotFound); ok {
return fmt.Errorf("%s. Please add the missing repos via 'helm repo add'", e.Error())
}
@ -77,9 +83,7 @@ func newDependencyBuildCmd(cfg *action.Configuration, out io.Writer) *cobra.Comm
}
f := cmd.Flags()
f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures")
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.BoolVar(&client.SkipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
addDependencySubcommandFlags(f, client)
return cmd
}

@ -16,6 +16,7 @@ limitations under the License.
package main
import (
"fmt"
"io"
"path/filepath"
@ -43,7 +44,7 @@ in the Chart.yaml file, but (b) at the wrong version.
`
// newDependencyUpdateCmd creates a new dependency update command.
func newDependencyUpdateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
func newDependencyUpdateCmd(_ *action.Configuration, out io.Writer) *cobra.Command {
client := action.NewDependency()
cmd := &cobra.Command{
@ -57,13 +58,19 @@ func newDependencyUpdateCmd(cfg *action.Configuration, out io.Writer) *cobra.Com
if len(args) > 0 {
chartpath = filepath.Clean(args[0])
}
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
man := &downloader.Manager{
Out: out,
ChartPath: chartpath,
Keyring: client.Keyring,
SkipUpdate: client.SkipRefresh,
Getters: getter.All(settings),
RegistryClient: cfg.RegistryClient,
RegistryClient: registryClient,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
@ -76,9 +83,7 @@ func newDependencyUpdateCmd(cfg *action.Configuration, out io.Writer) *cobra.Com
}
f := cmd.Flags()
f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures")
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.BoolVar(&client.SkipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
addDependencySubcommandFlags(f, client)
return cmd
}

@ -22,6 +22,7 @@ import (
"log"
"github.com/spf13/cobra"
k8sLabels "k8s.io/apimachinery/pkg/labels"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
@ -78,10 +79,13 @@ func (w metadataWriter) WriteTable(out io.Writer) error {
_, _ = fmt.Fprintf(out, "CHART: %v\n", w.metadata.Chart)
_, _ = fmt.Fprintf(out, "VERSION: %v\n", w.metadata.Version)
_, _ = fmt.Fprintf(out, "APP_VERSION: %v\n", w.metadata.AppVersion)
_, _ = fmt.Fprintf(out, "ANNOTATIONS: %v\n", k8sLabels.Set(w.metadata.Annotations).String())
_, _ = fmt.Fprintf(out, "DEPENDENCIES: %v\n", w.metadata.FormattedDepNames())
_, _ = fmt.Fprintf(out, "NAMESPACE: %v\n", w.metadata.Namespace)
_, _ = fmt.Fprintf(out, "REVISION: %v\n", w.metadata.Revision)
_, _ = fmt.Fprintf(out, "STATUS: %v\n", w.metadata.Status)
_, _ = fmt.Fprintf(out, "DEPLOYED_AT: %v\n", w.metadata.DeployedAt)
return nil
}

@ -141,7 +141,7 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
},
RunE: func(_ *cobra.Command, args []string) error {
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
@ -184,7 +184,7 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
f.Lookup("dry-run").NoOptDefVal = "client"
f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
f.BoolVar(&client.Replace, "replace", false, "reuse the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout")
f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")

@ -96,7 +96,7 @@ func TestInstall(t *testing.T) {
golden: "output/install-no-args.txt",
wantError: true,
},
// Install, re-use name
// Install, reuse name
{
name: "install and replace release",
cmd: "install aeneas testdata/testcharts/empty --replace",

@ -47,7 +47,7 @@ If '--keyring' is not specified, Helm usually defaults to the public keyring
unless your environment is otherwise configured.
`
func newPackageCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
func newPackageCmd(out io.Writer) *cobra.Command {
client := action.NewPackage()
valueOpts := &values.Options{}
@ -75,6 +75,12 @@ func newPackageCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return err
}
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
for i := 0; i < len(args); i++ {
path, err := filepath.Abs(args[i])
if err != nil {
@ -91,7 +97,7 @@ func newPackageCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Keyring: client.Keyring,
Getters: p,
Debug: settings.Debug,
RegistryClient: cfg.RegistryClient,
RegistryClient: registryClient,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
}
@ -119,6 +125,13 @@ func newPackageCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.StringVar(&client.AppVersion, "app-version", "", "set the appVersion on the chart to this version")
f.StringVarP(&client.Destination, "destination", "d", ".", "location to write the chart.")
f.BoolVarP(&client.DependencyUpdate, "dependency-update", "u", false, `update dependencies from "Chart.yaml" to dir "charts/" before packaging`)
f.StringVar(&client.Username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&client.Password, "password", "", "chart repository password where to locate the requested chart")
f.StringVar(&client.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&client.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.BoolVar(&client.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download")
f.BoolVar(&client.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download")
f.StringVar(&client.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
return cmd
}

@ -47,19 +47,27 @@ func newPluginCmd(out io.Writer) *cobra.Command {
// runHook will execute a plugin hook.
func runHook(p *plugin.Plugin, event string) error {
hook := p.Metadata.Hooks[event]
if hook == "" {
plugin.SetupPluginEnv(settings, p.Metadata.Name, p.Dir)
cmds := p.Metadata.PlatformHooks[event]
expandArgs := true
if len(cmds) == 0 && len(p.Metadata.Hooks) > 0 {
cmd := p.Metadata.Hooks[event]
if len(cmd) > 0 {
cmds = []plugin.PlatformCommand{{Command: "sh", Args: []string{"-c", cmd}}}
expandArgs = false
}
}
main, argv, err := plugin.PrepareCommands(cmds, expandArgs, []string{})
if err != nil {
return nil
}
prog := exec.Command("sh", "-c", hook)
// TODO make this work on windows
// I think its ... ¯\_(ツ)_/¯
// prog := exec.Command("cmd", "/C", p.Metadata.Hooks.Install())
prog := exec.Command(main, argv...)
debug("running %s hook: %s", event, prog)
plugin.SetupPluginEnv(settings, p.Metadata.Name, p.Dir)
prog.Stdout, prog.Stderr = os.Stdout, os.Stderr
if err := prog.Run(); err != nil {
if eerr, ok := err.(*exec.ExitError); ok {

@ -65,7 +65,7 @@ func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}

@ -40,6 +40,8 @@ type registryPushOptions struct {
caFile string
insecureSkipTLSverify bool
plainHTTP bool
password string
username string
}
func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
@ -68,7 +70,10 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return noMoreArgsComp()
},
RunE: func(_ *cobra.Command, args []string) error {
registryClient, err := newRegistryClient(o.certFile, o.keyFile, o.caFile, o.insecureSkipTLSverify, o.plainHTTP)
registryClient, err := newRegistryClient(
o.certFile, o.keyFile, o.caFile, o.insecureSkipTLSverify, o.plainHTTP, o.username, o.password,
)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
@ -96,6 +101,8 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&o.insecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart upload")
f.BoolVar(&o.plainHTTP, "plain-http", false, "use insecure HTTP connections for the chart upload")
f.StringVar(&o.username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&o.password, "password", "", "chart repository password where to locate the requested chart")
return cmd
}

@ -21,6 +21,7 @@ import (
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
@ -29,6 +30,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd"
"helm.sh/helm/v3/internal/tlsutil"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/registry"
"helm.sh/helm/v3/pkg/repo"
@ -153,7 +155,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
flags.ParseErrorsWhitelist.UnknownFlags = true
flags.Parse(args)
registryClient, err := newDefaultRegistryClient(false)
registryClient, err := newDefaultRegistryClient(false, "", "")
if err != nil {
return nil, err
}
@ -167,7 +169,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
newPullCmd(actionConfig, out),
newShowCmd(actionConfig, out),
newLintCmd(out),
newPackageCmd(actionConfig, out),
newPackageCmd(out),
newRepoCmd(out),
newSearchCmd(out),
newVerifyCmd(out),
@ -255,27 +257,30 @@ func checkForExpiredRepos(repofile string) {
}
func newRegistryClient(certFile, keyFile, caFile string, insecureSkipTLSverify, plainHTTP bool) (*registry.Client, error) {
func newRegistryClient(
certFile, keyFile, caFile string, insecureSkipTLSverify, plainHTTP bool, username, password string,
) (*registry.Client, error) {
if certFile != "" && keyFile != "" || caFile != "" || insecureSkipTLSverify {
registryClient, err := newRegistryClientWithTLS(certFile, keyFile, caFile, insecureSkipTLSverify)
registryClient, err := newRegistryClientWithTLS(certFile, keyFile, caFile, insecureSkipTLSverify, username, password)
if err != nil {
return nil, err
}
return registryClient, nil
}
registryClient, err := newDefaultRegistryClient(plainHTTP)
registryClient, err := newDefaultRegistryClient(plainHTTP, username, password)
if err != nil {
return nil, err
}
return registryClient, nil
}
func newDefaultRegistryClient(plainHTTP bool) (*registry.Client, error) {
func newDefaultRegistryClient(plainHTTP bool, username, password string) (*registry.Client, error) {
opts := []registry.ClientOption{
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(os.Stderr),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
registry.ClientOptBasicAuth(username, password),
}
if plainHTTP {
opts = append(opts, registry.ClientOptPlainHTTP())
@ -289,10 +294,26 @@ func newDefaultRegistryClient(plainHTTP bool) (*registry.Client, error) {
return registryClient, nil
}
func newRegistryClientWithTLS(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*registry.Client, error) {
func newRegistryClientWithTLS(
certFile, keyFile, caFile string, insecureSkipTLSverify bool, username, password string,
) (*registry.Client, error) {
tlsConf, err := tlsutil.NewClientTLS(certFile, keyFile, caFile, insecureSkipTLSverify)
if err != nil {
return nil, fmt.Errorf("can't create TLS config for client: %w", err)
}
// Create a new registry client
registryClient, err := registry.NewRegistryClientWithTLS(os.Stderr, certFile, keyFile, caFile, insecureSkipTLSverify,
settings.RegistryConfig, settings.Debug,
registryClient, err := registry.NewClient(
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(os.Stderr),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
registry.ClientOptHTTPClient(&http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConf,
},
}),
registry.ClientOptBasicAuth(username, password),
)
if err != nil {
return nil, err

@ -226,7 +226,7 @@ func runShow(args []string, client *action.Show) (string, error) {
func addRegistryClient(client *action.Show) error {
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}

@ -74,7 +74,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}

@ -1 +1 @@
{"name":"thomas-guide","chart":"foo","version":"0.1.0-beta.1","appVersion":"1.0","namespace":"default","revision":1,"status":"deployed","deployedAt":"1977-09-02T22:04:05Z"}
{"name":"thomas-guide","chart":"foo","version":"0.1.0-beta.1","appVersion":"1.0","annotations":{"category":"web-apps","supported":"true"},"dependencies":[{"name":"cool-plugin","version":"1.0.0","repository":"https://coolplugin.io/charts","condition":"coolPlugin.enabled","enabled":true},{"name":"crds","version":"2.7.1","repository":"","condition":"crds.enabled"}],"namespace":"default","revision":1,"status":"deployed","deployedAt":"1977-09-02T22:04:05Z"}

@ -2,6 +2,8 @@ NAME: thomas-guide
CHART: foo
VERSION: 0.1.0-beta.1
APP_VERSION: 1.0
ANNOTATIONS: category=web-apps,supported=true
DEPENDENCIES: cool-plugin,crds
NAMESPACE: default
REVISION: 1
STATUS: deployed

@ -1,5 +1,18 @@
annotations:
category: web-apps
supported: "true"
appVersion: "1.0"
chart: foo
dependencies:
- condition: coolPlugin.enabled
enabled: true
name: cool-plugin
repository: https://coolplugin.io/charts
version: 1.0.0
- condition: crds.enabled
name: crds
repository: ""
version: 2.7.1
deployedAt: "1977-09-02T22:04:05Z"
name: thomas-guide
namespace: default

@ -1,8 +1,8 @@
==> Linting testdata/testcharts/chart-with-bad-subcharts
[INFO] Chart.yaml: icon is recommended
[ERROR] templates/: error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
[ERROR] templates/: error unpacking subchart bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
[ERROR] : unable to load chart
error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
error unpacking subchart bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
==> Linting testdata/testcharts/chart-with-bad-subcharts/charts/bad-subchart
[ERROR] Chart.yaml: name is required

@ -1,7 +1,7 @@
==> Linting testdata/testcharts/chart-with-bad-subcharts
[INFO] Chart.yaml: icon is recommended
[ERROR] templates/: error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
[ERROR] templates/: error unpacking subchart bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
[ERROR] : unable to load chart
error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
error unpacking subchart bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
Error: 1 chart(s) linted, 1 chart(s) failed

@ -103,7 +103,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client.Namespace = settings.Namespace()
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}

@ -11,9 +11,9 @@ require (
github.com/Masterminds/squirrel v1.5.4
github.com/Masterminds/vcs v1.13.3
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/containerd/containerd v1.7.23
github.com/containerd/containerd v1.7.24
github.com/cyphar/filepath-securejoin v0.3.4
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2
github.com/distribution/distribution/v3 v3.0.0-rc.1
github.com/evanphx/json-patch v5.9.0+incompatible
github.com/foxcpp/go-mockdns v1.1.0
github.com/gobwas/glob v0.2.3
@ -32,19 +32,19 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/crypto v0.28.0
golang.org/x/term v0.25.0
golang.org/x/text v0.19.0
k8s.io/api v0.31.1
k8s.io/apiextensions-apiserver v0.31.1
k8s.io/apimachinery v0.31.1
k8s.io/apiserver v0.31.1
k8s.io/cli-runtime v0.31.1
k8s.io/client-go v0.31.1
golang.org/x/crypto v0.30.0
golang.org/x/term v0.27.0
golang.org/x/text v0.21.0
k8s.io/api v0.31.3
k8s.io/apiextensions-apiserver v0.31.3
k8s.io/apimachinery v0.31.3
k8s.io/apiserver v0.31.3
k8s.io/cli-runtime v0.31.3
k8s.io/client-go v0.31.3
k8s.io/klog/v2 v2.130.1
k8s.io/kubectl v0.31.1
k8s.io/kubectl v0.31.3
oras.land/oras-go v1.2.5
sigs.k8s.io/yaml v1.4.0
)
@ -54,29 +54,27 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 // indirect
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd // indirect
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b // indirect
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/containerd/errdefs v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v25.0.1+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v25.0.6+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fatih/color v1.13.0 // indirect
@ -91,25 +89,26 @@ require (
github.com/go-openapi/swag v0.22.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/gomodule/redigo v1.8.2 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
@ -130,10 +129,13 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_golang v1.20.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect
github.com/redis/go-redis/v9 v9.1.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
@ -141,29 +143,44 @@ require (
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 // indirect
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 // indirect
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
go.opentelemetry.io/contrib/bridges/prometheus v0.54.0 // indirect
go.opentelemetry.io/contrib/exporters/autoexport v0.54.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.5.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.29.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.29.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 // indirect
go.opentelemetry.io/otel/exporters/prometheus v0.51.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.5.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.29.0 // indirect
go.opentelemetry.io/otel/log v0.5.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/sdk v1.29.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.5.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect
go.opentelemetry.io/otel/trace v1.29.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/time v0.6.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
google.golang.org/grpc v1.65.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.66.2 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/component-base v0.31.1 // indirect
k8s.io/component-base v0.31.3 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect

209
go.sum

@ -28,8 +28,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ=
github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@ -40,19 +38,19 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0=
github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
@ -63,8 +61,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ=
github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
github.com/containerd/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA=
github.com/containerd/containerd v1.7.24/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
@ -73,6 +71,8 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@ -84,8 +84,10 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/distribution/distribution/v3 v3.0.0-rc.1 h1:6M4ewmPBUhF7wtQ8URLOQ1W/PQuVKiD1u8ymwLDUGqQ=
github.com/distribution/distribution/v3 v3.0.0-rc.1/go.mod h1:tFjaPDeHCrLg28e4feBIy27cP+qmrc/mvkl6MFIfVi4=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v25.0.1+incompatible h1:mFpqnrS6Hsm3v1k7Wa/BO23oz0k121MTbTO1lpcGSkU=
@ -94,8 +96,8 @@ github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBi
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg=
github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
@ -114,7 +116,6 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
@ -149,6 +150,7 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@ -169,8 +171,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
@ -193,23 +193,27 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw=
github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU=
github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4=
github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
@ -228,8 +232,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@ -239,6 +243,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
@ -270,8 +276,6 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
@ -321,8 +325,8 @@ github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjz
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_golang v1.20.1 h1:IMJXHOD6eARkQpxo8KkhgEVFlBNm+nkrFUyGlIu7Na8=
github.com/prometheus/client_golang v1.20.1/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -337,6 +341,13 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho=
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U=
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc=
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ=
github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY=
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlXpTI=
@ -364,13 +375,12 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@ -385,22 +395,50 @@ github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.opentelemetry.io/contrib/bridges/prometheus v0.54.0 h1:WWL67oxtknNVMb70lJXxXruf8UyK/a9hmIE1XO3Uedg=
go.opentelemetry.io/contrib/bridges/prometheus v0.54.0/go.mod h1:LqNcnXmyULp8ertk4hUTVtSUvKXj4h1Mx7gUCSSr/q0=
go.opentelemetry.io/contrib/exporters/autoexport v0.54.0 h1:dTmcmVm4J54IRPGm5oVjLci1uYat4UDea84E2tyBaAk=
go.opentelemetry.io/contrib/exporters/autoexport v0.54.0/go.mod h1:zPp5Fwpq2Hc7xMtVttg6GhZMcfTESjVbY9ONw2o/Dc4=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.5.0 h1:4d++HQ+Ihdl+53zSjtsCUFDmNMju2FC9qFkUlTxPLqo=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.5.0/go.mod h1:mQX5dTO3Mh5ZF7bPKDkt5c/7C41u/SiDr9XgTpzXXn8=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.29.0 h1:k6fQVDQexDE+3jG2SfCQjnHS7OamcP73YMoxEVq5B6k=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.29.0/go.mod h1:t4BrYLHU450Zo9fnydWlIuswB1bm7rM8havDpWOJeDo=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.29.0 h1:xvhQxJ/C9+RTnAj5DpTg7LSM1vbbMTiXt7e9hsfqHNw=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.29.0/go.mod h1:Fcvs2Bz1jkDM+Wf5/ozBGmi3tQ/c9zPKLnsipnfhGAo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0 h1:nSiV3s7wiCam610XcLbYOmMfJxB9gO4uK3Xgv5gmTgg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0/go.mod h1:hKn/e/Nmd19/x1gvIHwtOwVWM+VhuITSWip3JUDghj0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 h1:JAv0Jwtl01UFiyWZEMiJZBiTlv5A50zNs8lsthXqIio=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0/go.mod h1:QNKLmUEAq2QUbPQUfvw4fmv0bgbK7UlOSFCnXyfvSNc=
go.opentelemetry.io/otel/exporters/prometheus v0.51.0 h1:G7uexXb/K3T+T9fNLCCKncweEtNEBMTO+46hKX5EdKw=
go.opentelemetry.io/otel/exporters/prometheus v0.51.0/go.mod h1:v0mFe5Kk7woIh938mrZBJBmENYquyA0IICrlYm4Y0t4=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.5.0 h1:ThVXnEsdwNcxdBO+r96ci1xbF+PgNjwlk457VNuJODo=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.5.0/go.mod h1:rHWcSmC4q2h3gje/yOq6sAOaq8+UHxN/Ru3BbmDXOfY=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.29.0 h1:X3ZjNp36/WlkSYx0ul2jw4PtbNEDDeLskw3VPsrpYM0=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.29.0/go.mod h1:2uL/xnOXh0CHOBFCWXz5u1A4GXLiW+0IQIzVbeOEQ0U=
go.opentelemetry.io/otel/log v0.5.0 h1:x1Pr6Y3gnXgl1iFBwtGy1W/mnzENoK0w0ZoaeOI3i30=
go.opentelemetry.io/otel/log v0.5.0/go.mod h1:NU/ozXeGuOR5/mjCRXYbTC00NFJ3NYuraV/7O78F0rE=
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo=
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
go.opentelemetry.io/otel/sdk/log v0.5.0 h1:A+9lSjlZGxkQOr7QSBJcuyyYBw79CufQ69saiJLey7o=
go.opentelemetry.io/otel/sdk/log v0.5.0/go.mod h1:zjxIW7sw1IHolZL2KlSAtrUi8JHttoeiQy43Yl3WuVQ=
go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY=
go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY=
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@ -413,8 +451,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -444,11 +482,11 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -460,8 +498,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -484,8 +522,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -494,8 +532,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@ -503,10 +541,10 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -530,13 +568,15 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -556,7 +596,6 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWM
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
@ -567,26 +606,26 @@ gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40=
k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ=
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c=
k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM=
k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk=
k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U=
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8=
k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w=
k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8=
k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE=
k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE=
k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4=
k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4=
k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/apiserver v0.31.3 h1:+1oHTtCB+OheqFEz375D0IlzHZ5VeQKX1KGXnx+TTuY=
k8s.io/apiserver v0.31.3/go.mod h1:PrxVbebxrxQPFhJk4powDISIROkNMKHibTg9lTRQ0Qg=
k8s.io/cli-runtime v0.31.3 h1:fEQD9Xokir78y7pVK/fCJN090/iYNrLHpFbGU4ul9TI=
k8s.io/cli-runtime v0.31.3/go.mod h1:Q2jkyTpl+f6AtodQvgDI8io3jrfr+Z0LyQBPJJ2Btq8=
k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4=
k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs=
k8s.io/component-base v0.31.3 h1:DMCXXVx546Rfvhj+3cOm2EUxhS+EyztH423j+8sOwhQ=
k8s.io/component-base v0.31.3/go.mod h1:xME6BHfUOafRgT0rGVBGl7TuSg8Z9/deT7qq6w7qjIU=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/kubectl v0.31.1 h1:ih4JQJHxsEggFqDJEHSOdJ69ZxZftgeZvYo7M/cpp24=
k8s.io/kubectl v0.31.1/go.mod h1:aNuQoR43W6MLAtXQ/Bu4GDmoHlbhHKuyD49lmTC8eJM=
k8s.io/kubectl v0.31.3 h1:3r111pCjPsvnR98oLLxDMwAeM6OPGmPty6gSKaLTQes=
k8s.io/kubectl v0.31.3/go.mod h1:lhMECDCbJN8He12qcKqs2QfmVo9Pue30geovBVpH5fs=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=

@ -394,8 +394,8 @@ func (cfg *Configuration) Init(getter genericclioptions.RESTClientGetter, namesp
if cfg.Releases != nil {
if mem, ok := cfg.Releases.Driver.(*driver.Memory); ok {
// This function can be called more than once (e.g., helm list --all-namespaces).
// If a memory driver was already initialized, re-use it but set the possibly new namespace.
// We re-use it in case some releases where already created in the existing memory driver.
// If a memory driver was already initialized, reuse it but set the possibly new namespace.
// We reuse it in case some releases where already created in the existing memory driver.
d = mem
}
}

@ -34,10 +34,17 @@ import (
//
// It provides the implementation of 'helm dependency' and its respective subcommands.
type Dependency struct {
Verify bool
Keyring string
SkipRefresh bool
ColumnWidth uint
Verify bool
Keyring string
SkipRefresh bool
ColumnWidth uint
Username string
Password string
CertFile string
KeyFile string
CaFile string
InsecureSkipTLSverify bool
PlainHTTP bool
}
// NewDependency creates a new Dependency object with the given configuration.

@ -16,7 +16,13 @@ limitations under the License.
package action
import "time"
import (
"sort"
"strings"
"time"
"helm.sh/helm/v3/pkg/chart"
)
// GetMetadata is the action for checking a given release's metadata.
//
@ -28,14 +34,16 @@ type GetMetadata struct {
}
type Metadata struct {
Name string `json:"name" yaml:"name"`
Chart string `json:"chart" yaml:"chart"`
Version string `json:"version" yaml:"version"`
AppVersion string `json:"appVersion" yaml:"appVersion"`
Namespace string `json:"namespace" yaml:"namespace"`
Revision int `json:"revision" yaml:"revision"`
Status string `json:"status" yaml:"status"`
DeployedAt string `json:"deployedAt" yaml:"deployedAt"`
Name string `json:"name" yaml:"name"`
Chart string `json:"chart" yaml:"chart"`
Version string `json:"version" yaml:"version"`
AppVersion string `json:"appVersion" yaml:"appVersion"`
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
Dependencies []*chart.Dependency `json:"dependencies,omitempty" yaml:"dependencies,omitempty"`
Namespace string `json:"namespace" yaml:"namespace"`
Revision int `json:"revision" yaml:"revision"`
Status string `json:"status" yaml:"status"`
DeployedAt string `json:"deployedAt" yaml:"deployedAt"`
}
// NewGetMetadata creates a new GetMetadata object with the given configuration.
@ -57,13 +65,26 @@ func (g *GetMetadata) Run(name string) (*Metadata, error) {
}
return &Metadata{
Name: rel.Name,
Chart: rel.Chart.Metadata.Name,
Version: rel.Chart.Metadata.Version,
AppVersion: rel.Chart.Metadata.AppVersion,
Namespace: rel.Namespace,
Revision: rel.Version,
Status: rel.Info.Status.String(),
DeployedAt: rel.Info.LastDeployed.Format(time.RFC3339),
Name: rel.Name,
Chart: rel.Chart.Metadata.Name,
Version: rel.Chart.Metadata.Version,
AppVersion: rel.Chart.Metadata.AppVersion,
Dependencies: rel.Chart.Metadata.Dependencies,
Annotations: rel.Chart.Metadata.Annotations,
Namespace: rel.Namespace,
Revision: rel.Version,
Status: rel.Info.Status.String(),
DeployedAt: rel.Info.LastDeployed.Format(time.RFC3339),
}, nil
}
// FormattedDepNames formats metadata.dependencies names into a comma-separated list.
func (m *Metadata) FormattedDepNames() string {
depsNames := make([]string, 0, len(m.Dependencies))
for _, dep := range m.Dependencies {
depsNames = append(depsNames, dep.Name)
}
sort.StringSlice(depsNames).Sort()
return strings.Join(depsNames, ",")
}

@ -99,7 +99,8 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
// If all hooks are successful, check the annotation of each hook to determine whether the hook should be deleted
// under succeeded condition. If so, then clear the corresponding resource object in each hook
for _, h := range executingHooks {
for i := len(executingHooks) - 1; i >= 0; i-- {
h := executingHooks[i]
if err := cfg.deleteHookByPolicy(h, release.HookSucceeded, timeout); err != nil {
return err
}

@ -450,7 +450,7 @@ func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.Resource
}
// 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
// do an update, but it's not clear whether we WANT to do an update if the reuse is set
// to true, since that is basically an upgrade operation.
if len(toBeAdopted) == 0 && len(resources) > 0 {
_, err = i.cfg.KubeClient.Create(resources)
@ -544,7 +544,7 @@ func (i *Install) availableName() error {
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")
return errors.New("cannot reuse a name that is still in use")
}
// createRelease creates a new release object
@ -574,7 +574,7 @@ func (i *Install) recordRelease(r *release.Release) error {
// replaceRelease replaces an older release with this one
//
// This allows us to re-use names by superseding an existing release with a new one
// This allows us to reuse 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 {
@ -770,6 +770,7 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
getter.WithTLSClientConfig(c.CertFile, c.KeyFile, c.CaFile),
getter.WithInsecureSkipVerifyTLS(c.InsecureSkipTLSverify),
getter.WithPlainHTTP(c.PlainHTTP),
getter.WithBasicAuth(c.Username, c.Password),
},
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,

@ -44,8 +44,15 @@ type Package struct {
Destination string
DependencyUpdate bool
RepositoryConfig string
RepositoryCache string
RepositoryConfig string
RepositoryCache string
PlainHTTP bool
Username string
Password string
CertFile string
KeyFile string
CaFile string
InsecureSkipTLSverify bool
}
// NewPackage creates a new Package object with the given configuration.

@ -73,7 +73,7 @@ func WithPlainHTTP(plainHTTP bool) PushOpt {
}
}
// WithOptWriter sets the registryOut field on the push configuration object.
// WithPushOptWriter sets the registryOut field on the push configuration object.
func WithPushOptWriter(out io.Writer) PushOpt {
return func(p *Push) {
p.out = out

@ -83,7 +83,7 @@ type Upgrade struct {
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 will reuse the user's last supplied values.
ReuseValues bool
// ResetThenReuseValues will reset the values to the chart's built-ins then merge with user's last supplied values.
ResetThenReuseValues bool

@ -25,28 +25,28 @@ type Dependency struct {
// Name is the name of the dependency.
//
// This must mach the name in the dependency's Chart.yaml.
Name string `json:"name"`
Name string `json:"name" yaml:"name"`
// Version is the version (range) of this chart.
//
// A lock file will always produce a single version, while a dependency
// may contain a semantic version range.
Version string `json:"version,omitempty"`
Version string `json:"version,omitempty" yaml:"version,omitempty"`
// The URL to the repository.
//
// Appending `index.yaml` to this string should result in a URL that can be
// used to fetch the repository index.
Repository string `json:"repository"`
Repository string `json:"repository" yaml:"repository"`
// A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled )
Condition string `json:"condition,omitempty"`
Condition string `json:"condition,omitempty" yaml:"condition,omitempty"`
// Tags can be used to group charts for enabling/disabling together
Tags []string `json:"tags,omitempty"`
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
// Enabled bool determines if chart should be loaded
Enabled bool `json:"enabled,omitempty"`
Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty"`
// ImportValues holds the mapping of source values to parent key to be imported. Each item can be a
// string or pair of child/parent sublist items.
ImportValues []interface{} `json:"import-values,omitempty"`
ImportValues []interface{} `json:"import-values,omitempty" yaml:"import-values,omitempty"`
// Alias usable alias to be used for the chart
Alias string `json:"alias,omitempty"`
Alias string `json:"alias,omitempty" yaml:"alias,omitempty"`
}
// Validate checks for common problems with the dependency datastructure in

@ -174,7 +174,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
case filepath.Ext(n) == ".tgz":
file := files[0]
if file.Name != n {
return c, errors.Errorf("error unpacking tar in %s: expected %s, got %s", c.Name(), n, file.Name)
return c, errors.Errorf("error unpacking subchart tar in %s: expected %s, got %s", c.Name(), n, file.Name)
}
// Untar the chart and add to c.Dependencies
sc, err = LoadArchive(bytes.NewBuffer(file.Data))
@ -194,7 +194,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
}
if err != nil {
return c, errors.Wrapf(err, "error unpacking %s in %s", n, c.Name())
return c, errors.Wrapf(err, "error unpacking subchart %s in %s", n, c.Name())
}
c.AddDependency(sc)
}

@ -3,7 +3,7 @@ kind: Service
metadata:
name: subchart2-{{ .Chart.Name }}
labels:
helm.sh/hart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
type: {{ .Values.service.type }}
ports:

@ -18,7 +18,7 @@ package chartutil
import "testing"
// TestValidateName is a regression test for ValidateName
// TestValidateReleaseName is a regression test for ValidateName
//
// Kubernetes has strict naming conventions for resource names. This test represents
// those conventions.

@ -96,6 +96,8 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
return "", nil, err
}
c.Options = append(c.Options, getter.WithAcceptHeader("application/gzip,application/octet-stream"))
data, err := g.Get(u.String(), c.Options...)
if err != nil {
return "", nil, err

@ -38,6 +38,7 @@ type options struct {
unTar bool
insecureSkipVerifyTLS bool
plainHTTP bool
acceptHeader string
username string
password string
passCredentialsAll bool
@ -60,6 +61,13 @@ func WithURL(url string) Option {
}
}
// WithAcceptHeader sets the request's Accept header as some REST APIs serve multiple content types
func WithAcceptHeader(header string) Option {
return func(opts *options) {
opts.acceptHeader = header
}
}
// WithBasicAuth sets the request's Authorization header to use the provided credentials
func WithBasicAuth(username, password string) Option {
return func(opts *options) {

@ -53,6 +53,10 @@ func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) {
return nil, err
}
if g.opts.acceptHeader != "" {
req.Header.Set("Accept", g.opts.acceptHeader)
}
req.Header.Set("User-Agent", version.GetUserAgent())
if g.opts.userAgent != "" {
req.Header.Set("User-Agent", g.opts.userAgent)

@ -280,6 +280,29 @@ func TestDownload(t *testing.T) {
if got.String() != expect {
t.Errorf("Expected %q, got %q", expect, got.String())
}
// test server with varied Accept Header
const expectedAcceptHeader = "application/gzip,application/octet-stream"
acceptHeaderSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Accept") != expectedAcceptHeader {
t.Errorf("Expected '%s', got '%s'", expectedAcceptHeader, r.Header.Get("Accept"))
}
fmt.Fprint(w, expect)
}))
defer acceptHeaderSrv.Close()
u, _ = url.ParseRequestURI(acceptHeaderSrv.URL)
httpgetter, err = NewHTTPGetter(
WithAcceptHeader(expectedAcceptHeader),
)
if err != nil {
t.Fatal(err)
}
_, err = httpgetter.Get(u.String())
if err != nil {
t.Fatal(err)
}
}
func TestDownloadTLS(t *testing.T) {

@ -22,6 +22,7 @@ import (
"net/http"
"strings"
"testing"
"time"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -452,6 +453,195 @@ func TestPerform(t *testing.T) {
}
}
func TestWait(t *testing.T) {
podList := newPodList("starfish", "otter", "squid")
var created *time.Time
c := newTestClient(t)
c.Factory.(*cmdtesting.TestFactory).Client = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
p, m := req.URL.Path, req.Method
t.Logf("got request %s %s", p, m)
switch {
case p == "/api/v1/namespaces/default/pods/starfish" && m == "GET":
pod := &podList.Items[0]
if created != nil && time.Since(*created) >= time.Second*5 {
pod.Status.Conditions = []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
}
}
return newResponse(200, pod)
case p == "/api/v1/namespaces/default/pods/otter" && m == "GET":
pod := &podList.Items[1]
if created != nil && time.Since(*created) >= time.Second*5 {
pod.Status.Conditions = []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
}
}
return newResponse(200, pod)
case p == "/api/v1/namespaces/default/pods/squid" && m == "GET":
pod := &podList.Items[2]
if created != nil && time.Since(*created) >= time.Second*5 {
pod.Status.Conditions = []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
}
}
return newResponse(200, pod)
case p == "/namespaces/default/pods" && m == "POST":
resources, err := c.Build(req.Body, false)
if err != nil {
t.Fatal(err)
}
now := time.Now()
created = &now
return newResponse(200, resources[0].Object)
default:
t.Fatalf("unexpected request: %s %s", req.Method, req.URL.Path)
return nil, nil
}
}),
}
resources, err := c.Build(objBody(&podList), false)
if err != nil {
t.Fatal(err)
}
result, err := c.Create(resources)
if err != nil {
t.Fatal(err)
}
if len(result.Created) != 3 {
t.Errorf("expected 3 resource created, got %d", len(result.Created))
}
if err := c.Wait(resources, time.Second*30); err != nil {
t.Errorf("expected wait without error, got %s", err)
}
if time.Since(*created) < time.Second*5 {
t.Errorf("expected to wait at least 5 seconds before ready status was detected, but got %s", time.Since(*created))
}
}
func TestWaitJob(t *testing.T) {
job := newJob("starfish", 0, intToInt32(1), 0, 0)
var created *time.Time
c := newTestClient(t)
c.Factory.(*cmdtesting.TestFactory).Client = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
p, m := req.URL.Path, req.Method
t.Logf("got request %s %s", p, m)
switch {
case p == "/apis/batch/v1/namespaces/default/jobs/starfish" && m == "GET":
if created != nil && time.Since(*created) >= time.Second*5 {
job.Status.Succeeded = 1
}
return newResponse(200, job)
case p == "/namespaces/default/jobs" && m == "POST":
resources, err := c.Build(req.Body, false)
if err != nil {
t.Fatal(err)
}
now := time.Now()
created = &now
return newResponse(200, resources[0].Object)
default:
t.Fatalf("unexpected request: %s %s", req.Method, req.URL.Path)
return nil, nil
}
}),
}
resources, err := c.Build(objBody(job), false)
if err != nil {
t.Fatal(err)
}
result, err := c.Create(resources)
if err != nil {
t.Fatal(err)
}
if len(result.Created) != 1 {
t.Errorf("expected 1 resource created, got %d", len(result.Created))
}
if err := c.WaitWithJobs(resources, time.Second*30); err != nil {
t.Errorf("expected wait without error, got %s", err)
}
if time.Since(*created) < time.Second*5 {
t.Errorf("expected to wait at least 5 seconds before ready status was detected, but got %s", time.Since(*created))
}
}
func TestWaitDelete(t *testing.T) {
pod := newPod("starfish")
var deleted *time.Time
c := newTestClient(t)
c.Factory.(*cmdtesting.TestFactory).Client = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
p, m := req.URL.Path, req.Method
t.Logf("got request %s %s", p, m)
switch {
case p == "/namespaces/default/pods/starfish" && m == "GET":
if deleted != nil && time.Since(*deleted) >= time.Second*5 {
return newResponse(404, notFoundBody())
}
return newResponse(200, &pod)
case p == "/namespaces/default/pods/starfish" && m == "DELETE":
now := time.Now()
deleted = &now
return newResponse(200, &pod)
case p == "/namespaces/default/pods" && m == "POST":
resources, err := c.Build(req.Body, false)
if err != nil {
t.Fatal(err)
}
return newResponse(200, resources[0].Object)
default:
t.Fatalf("unexpected request: %s %s", req.Method, req.URL.Path)
return nil, nil
}
}),
}
resources, err := c.Build(objBody(&pod), false)
if err != nil {
t.Fatal(err)
}
result, err := c.Create(resources)
if err != nil {
t.Fatal(err)
}
if len(result.Created) != 1 {
t.Errorf("expected 1 resource created, got %d", len(result.Created))
}
if _, err := c.Delete(resources); err != nil {
t.Fatal(err)
}
if err := c.WaitForDelete(resources, time.Second*30); err != nil {
t.Errorf("expected wait without error, got %s", err)
}
if time.Since(*deleted) < time.Second*5 {
t.Errorf("expected to wait at least 5 seconds before ready status was detected, but got %s", time.Since(*deleted))
}
}
func TestReal(t *testing.T) {
t.Skip("This is a live test, comment this line to run")
c := New(nil)

@ -30,13 +30,13 @@ import (
)
const (
badCharNametDir = "testdata/badchartname"
badChartNameDir = "testdata/badchartname"
badChartDir = "testdata/badchartfile"
anotherBadChartDir = "testdata/anotherbadchartfile"
)
var (
badChartNamePath = filepath.Join(badCharNametDir, "Chart.yaml")
badChartNamePath = filepath.Join(badChartNameDir, "Chart.yaml")
badChartFilePath = filepath.Join(badChartDir, "Chart.yaml")
nonExistingChartFilePath = filepath.Join(os.TempDir(), "Chart.yaml")
)

@ -25,5 +25,8 @@ const (
Update = "update"
)
// PlatformHooks is a map of events to a command for a particular operating system and architecture.
type PlatformHooks map[string][]PlatformCommand
// Hooks is a map of events to commands.
type Hooks map[string]string

@ -44,9 +44,10 @@ type Downloaders struct {
// PlatformCommand represents a command for a particular operating system and architecture
type PlatformCommand struct {
OperatingSystem string `json:"os"`
Architecture string `json:"arch"`
Command string `json:"command"`
OperatingSystem string `json:"os"`
Architecture string `json:"arch"`
Command string `json:"command"`
Args []string `json:"args"`
}
// Metadata describes a plugin.
@ -65,23 +66,35 @@ type Metadata struct {
// Description is a long description shown in places like `helm help`
Description string `json:"description"`
// Command is the command, as a single string.
// PlatformCommand is the plugin command, with a platform selector and support for args.
//
// The command will be passed through environment expansion, so env vars can
// The command and args will be passed through environment expansion, so env vars can
// be present in this command. Unless IgnoreFlags is set, this will
// also merge the flags passed from Helm.
//
// Note that command is not executed in a shell. To do so, we suggest
// Note that the command is not executed in a shell. To do so, we suggest
// pointing the command to a shell script.
//
// The following rules will apply to processing commands:
// - If platformCommand is present, it will be searched first
// The following rules will apply to processing platform commands:
// - If PlatformCommand is present, it will be used
// - If both OS and Arch match the current platform, search will stop and the command will be executed
// - If OS matches and there is no more specific match, the command will be executed
// - If OS matches and Arch is empty, the command will be executed
// - If no OS/Arch match is found, the default command will be executed
// - If no command is present and no matches are found in platformCommand, Helm will exit with an error
// - If no matches are found in platformCommand, Helm will exit with an error
PlatformCommand []PlatformCommand `json:"platformCommand"`
Command string `json:"command"`
// Command is the plugin command, as a single string.
// Providing a command will result in an error if PlatformCommand is also set.
//
// The command will be passed through environment expansion, so env vars can
// be present in this command. Unless IgnoreFlags is set, this will
// also merge the flags passed from Helm.
//
// Note that command is not executed in a shell. To do so, we suggest
// pointing the command to a shell script.
//
// DEPRECATED: Use PlatformCommand instead. Remove in Helm 4.
Command string `json:"command"`
// IgnoreFlags ignores any flags passed in from Helm
//
@ -90,7 +103,31 @@ type Metadata struct {
// the `--debug` flag will be discarded.
IgnoreFlags bool `json:"ignoreFlags"`
// Hooks are commands that will run on events.
// PlatformHooks are commands that will run on plugin events, with a platform selector and support for args.
//
// The command and args will be passed through environment expansion, so env vars can
// be present in the command.
//
// Note that the command is not executed in a shell. To do so, we suggest
// pointing the command to a shell script.
//
// The following rules will apply to processing platform hooks:
// - If PlatformHooks is present, it will be used
// - If both OS and Arch match the current platform, search will stop and the command will be executed
// - If OS matches and Arch is empty, the command will be executed
// - If no OS/Arch match is found, the default command will be executed
// - If no matches are found in platformHooks, Helm will skip the event
PlatformHooks PlatformHooks `json:"platformHooks"`
// Hooks are commands that will run on plugin events, as a single string.
// Providing a hooks will result in an error if PlatformHooks is also set.
//
// The command will be passed through environment expansion, so env vars can
// be present in this command.
//
// Note that the command is executed in the sh shell.
//
// DEPRECATED: Use PlatformHooks instead. Remove in Helm 4.
Hooks Hooks
// Downloaders field is used if the plugin supply downloader mechanism
@ -112,62 +149,106 @@ type Plugin struct {
Dir string
}
// The following rules will apply to processing the Plugin.PlatformCommand.Command:
// - If both OS and Arch match the current platform, search will stop and the command will be prepared for execution
// - If OS matches and there is no more specific match, the command will be prepared for execution
// - If no OS/Arch match is found, return nil
func getPlatformCommand(cmds []PlatformCommand) []string {
var command []string
// Returns command and args strings based on the following rules in priority order:
// - From the PlatformCommand where OS and Arch match the current platform
// - From the PlatformCommand where OS matches the current platform and Arch is empty/unspecified
// - From the PlatformCommand where OS is empty/unspecified and Arch matches the current platform
// - From the PlatformCommand where OS and Arch are both empty/unspecified
// - Return nil, nil
func getPlatformCommand(cmds []PlatformCommand) ([]string, []string) {
var command, args []string
found := false
foundOs := false
eq := strings.EqualFold
for _, c := range cmds {
if eq(c.OperatingSystem, runtime.GOOS) {
command = strings.Split(c.Command, " ")
}
if eq(c.OperatingSystem, runtime.GOOS) && eq(c.Architecture, runtime.GOARCH) {
return strings.Split(c.Command, " ")
// Return early for an exact match
return strings.Split(c.Command, " "), c.Args
}
if (len(c.OperatingSystem) > 0 && !eq(c.OperatingSystem, runtime.GOOS)) || len(c.Architecture) > 0 {
// Skip if OS is not empty and doesn't match or if arch is set as a set arch requires an OS match
continue
}
if !foundOs && len(c.OperatingSystem) > 0 && eq(c.OperatingSystem, runtime.GOOS) {
// First OS match with empty arch, can only be overridden by a direct match
command = strings.Split(c.Command, " ")
args = c.Args
found = true
foundOs = true
} else if !found {
// First empty match, can be overridden by a direct match or an OS match
command = strings.Split(c.Command, " ")
args = c.Args
found = true
}
}
return command
return command, args
}
// PrepareCommand takes a Plugin.PlatformCommand.Command, a Plugin.Command and will applying the following processing:
// - If platformCommand is present, it will be searched first
// - If both OS and Arch match the current platform, search will stop and the command will be prepared for execution
// - If OS matches and there is no more specific match, the command will be prepared for execution
// - If no OS/Arch match is found, the default command will be prepared for execution
// - If no command is present and no matches are found in platformCommand, will exit with an error
// PrepareCommands takes a []Plugin.PlatformCommand
// and prepares the command and arguments for execution.
//
// It merges extraArgs into any arguments supplied in the plugin. It
// returns the name of the command and an args array.
// returns the main command and an args array.
//
// The result is suitable to pass to exec.Command.
func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string, error) {
var parts []string
platCmdLen := len(p.Metadata.PlatformCommand)
if platCmdLen > 0 {
parts = getPlatformCommand(p.Metadata.PlatformCommand)
}
if platCmdLen == 0 || parts == nil {
parts = strings.Split(p.Metadata.Command, " ")
}
if len(parts) == 0 || parts[0] == "" {
func PrepareCommands(cmds []PlatformCommand, expandArgs bool, extraArgs []string) (string, []string, error) {
cmdParts, args := getPlatformCommand(cmds)
if len(cmdParts) == 0 || cmdParts[0] == "" {
return "", nil, fmt.Errorf("no plugin command is applicable")
}
main := os.ExpandEnv(parts[0])
main := os.ExpandEnv(cmdParts[0])
baseArgs := []string{}
if len(parts) > 1 {
for _, cmdpart := range parts[1:] {
cmdexp := os.ExpandEnv(cmdpart)
baseArgs = append(baseArgs, cmdexp)
if len(cmdParts) > 1 {
for _, cmdPart := range cmdParts[1:] {
if expandArgs {
baseArgs = append(baseArgs, os.ExpandEnv(cmdPart))
} else {
baseArgs = append(baseArgs, cmdPart)
}
}
}
if !p.Metadata.IgnoreFlags {
for _, arg := range args {
if expandArgs {
baseArgs = append(baseArgs, os.ExpandEnv(arg))
} else {
baseArgs = append(baseArgs, arg)
}
}
if len(extraArgs) > 0 {
baseArgs = append(baseArgs, extraArgs...)
}
return main, baseArgs, nil
}
// PrepareCommand gets the correct command and arguments for a plugin.
//
// It merges extraArgs into any arguments supplied in the plugin. It returns the name of the command and an args array.
//
// The result is suitable to pass to exec.Command.
func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string, error) {
var extraArgsIn []string
if !p.Metadata.IgnoreFlags {
extraArgsIn = extraArgs
}
cmds := p.Metadata.PlatformCommand
if len(cmds) == 0 && len(p.Metadata.Command) > 0 {
cmds = []PlatformCommand{{Command: p.Metadata.Command}}
}
return PrepareCommands(cmds, true, extraArgsIn)
}
// validPluginName is a regular expression that validates plugin names.
//
// Plugin names can only contain the ASCII characters a-z, A-Z, 0-9, _ and -.
@ -184,6 +265,14 @@ func validatePluginData(plug *Plugin, filepath string) error {
}
plug.Metadata.Usage = sanitizeString(plug.Metadata.Usage)
if len(plug.Metadata.PlatformCommand) > 0 && len(plug.Metadata.Command) > 0 {
return fmt.Errorf("both platformCommand and command are set in %q", filepath)
}
if len(plug.Metadata.PlatformHooks) > 0 && len(plug.Metadata.Hooks) > 0 {
return fmt.Errorf("both platformHooks and hooks are set in %q", filepath)
}
// We could also validate SemVer, executable, and other fields should we so choose.
return nil
}

@ -26,160 +26,256 @@ import (
"helm.sh/helm/v3/pkg/cli"
)
func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) {
cmd, args, err := p.PrepareCommand(extraArgs)
if err != nil {
t.Fatal(err)
}
if cmd != "echo" {
t.Fatalf("Expected echo, got %q", cmd)
}
if l := len(args); l != 5 {
t.Fatalf("expected 5 args, got %d", l)
}
func TestPrepareCommand(t *testing.T) {
cmdMain := "sh"
cmdArgs := []string{"-c", "echo \"test\""}
expect := []string{"-n", osStrCmp, "--debug", "--foo", "bar"}
for i := 0; i < len(args); i++ {
if expect[i] != args[i] {
t.Errorf("Expected arg=%q, got %q", expect[i], args[i])
}
p := &Plugin{
Dir: "/tmp", // Unused
Metadata: &Metadata{
Name: "test",
Command: "echo \"error\"",
PlatformCommand: []PlatformCommand{
{OperatingSystem: "no-os", Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: "", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: runtime.GOARCH, Command: cmdMain, Args: cmdArgs},
},
},
}
// Test with IgnoreFlags. This should omit --debug, --foo, bar
p.Metadata.IgnoreFlags = true
cmd, args, err = p.PrepareCommand(extraArgs)
cmd, args, err := p.PrepareCommand([]string{})
if err != nil {
t.Fatal(err)
}
if cmd != "echo" {
t.Fatalf("Expected echo, got %q", cmd)
if cmd != cmdMain {
t.Fatalf("Expected %q, got %q", cmdMain, cmd)
}
if l := len(args); l != 2 {
t.Fatalf("expected 2 args, got %d", l)
}
expect = []string{"-n", osStrCmp}
for i := 0; i < len(args); i++ {
if expect[i] != args[i] {
t.Errorf("Expected arg=%q, got %q", expect[i], args[i])
}
if !reflect.DeepEqual(args, cmdArgs) {
t.Fatalf("Expected %v, got %v", cmdArgs, args)
}
}
func TestPrepareCommand(t *testing.T) {
func TestPrepareCommandExtraArgs(t *testing.T) {
cmdMain := "sh"
cmdArgs := []string{"-c", "echo \"test\""}
extraArgs := []string{"--debug", "--foo", "bar"}
p := &Plugin{
Dir: "/tmp", // Unused
Metadata: &Metadata{
Name: "test",
Command: "echo -n foo",
Command: "echo \"error\"",
PlatformCommand: []PlatformCommand{
{OperatingSystem: "no-os", Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: runtime.GOARCH, Command: cmdMain, Args: cmdArgs},
{OperatingSystem: runtime.GOOS, Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: "", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
},
},
}
argv := []string{"--debug", "--foo", "bar"}
checkCommand(p, argv, "foo", t)
expectedArgs := append(cmdArgs, extraArgs...)
cmd, args, err := p.PrepareCommand(extraArgs)
if err != nil {
t.Fatal(err)
}
if cmd != cmdMain {
t.Fatalf("Expected %q, got %q", cmdMain, cmd)
}
if !reflect.DeepEqual(args, expectedArgs) {
t.Fatalf("Expected %v, got %v", expectedArgs, args)
}
}
func TestPlatformPrepareCommand(t *testing.T) {
func TestPrepareCommandExtraArgsIgnored(t *testing.T) {
cmdMain := "sh"
cmdArgs := []string{"-c", "echo \"test\""}
extraArgs := []string{"--debug", "--foo", "bar"}
p := &Plugin{
Dir: "/tmp", // Unused
Metadata: &Metadata{
Name: "test",
Command: "echo -n os-arch",
Command: "echo \"error\"",
PlatformCommand: []PlatformCommand{
{OperatingSystem: "linux", Architecture: "386", Command: "echo -n linux-386"},
{OperatingSystem: "linux", Architecture: "amd64", Command: "echo -n linux-amd64"},
{OperatingSystem: "linux", Architecture: "arm64", Command: "echo -n linux-arm64"},
{OperatingSystem: "linux", Architecture: "ppc64le", Command: "echo -n linux-ppc64le"},
{OperatingSystem: "linux", Architecture: "s390x", Command: "echo -n linux-s390x"},
{OperatingSystem: "linux", Architecture: "riscv64", Command: "echo -n linux-riscv64"},
{OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"},
{OperatingSystem: "no-os", Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: runtime.GOARCH, Command: cmdMain, Args: cmdArgs},
{OperatingSystem: runtime.GOOS, Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: "", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
},
IgnoreFlags: true,
},
}
var osStrCmp string
os := runtime.GOOS
arch := runtime.GOARCH
if os == "linux" && arch == "386" {
osStrCmp = "linux-386"
} else if os == "linux" && arch == "amd64" {
osStrCmp = "linux-amd64"
} else if os == "linux" && arch == "arm64" {
osStrCmp = "linux-arm64"
} else if os == "linux" && arch == "ppc64le" {
osStrCmp = "linux-ppc64le"
} else if os == "linux" && arch == "s390x" {
osStrCmp = "linux-s390x"
} else if os == "linux" && arch == "riscv64" {
osStrCmp = "linux-riscv64"
} else if os == "windows" && arch == "amd64" {
osStrCmp = "win-64"
} else {
osStrCmp = "os-arch"
}
argv := []string{"--debug", "--foo", "bar"}
checkCommand(p, argv, osStrCmp, t)
cmd, args, err := p.PrepareCommand(extraArgs)
if err != nil {
t.Fatal(err)
}
if cmd != cmdMain {
t.Fatalf("Expected %q, got %q", cmdMain, cmd)
}
if !reflect.DeepEqual(args, cmdArgs) {
t.Fatalf("Expected %v, got %v", cmdArgs, args)
}
}
func TestPartialPlatformPrepareCommand(t *testing.T) {
p := &Plugin{
Dir: "/tmp", // Unused
Metadata: &Metadata{
Name: "test",
Command: "echo -n os-arch",
PlatformCommand: []PlatformCommand{
{OperatingSystem: "linux", Architecture: "386", Command: "echo -n linux-386"},
{OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"},
},
},
func TestPrepareCommands(t *testing.T) {
cmdMain := "sh"
cmdArgs := []string{"-c", "echo \"test\""}
cmds := []PlatformCommand{
{OperatingSystem: "no-os", Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: runtime.GOARCH, Command: cmdMain, Args: cmdArgs},
{OperatingSystem: runtime.GOOS, Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: "", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
}
cmd, args, err := PrepareCommands(cmds, true, []string{})
if err != nil {
t.Fatal(err)
}
if cmd != cmdMain {
t.Fatalf("Expected %q, got %q", cmdMain, cmd)
}
var osStrCmp string
os := runtime.GOOS
arch := runtime.GOARCH
if os == "linux" {
osStrCmp = "linux-386"
} else if os == "windows" && arch == "amd64" {
osStrCmp = "win-64"
} else {
osStrCmp = "os-arch"
if !reflect.DeepEqual(args, cmdArgs) {
t.Fatalf("Expected %v, got %v", cmdArgs, args)
}
}
func TestPrepareCommandsExtraArgs(t *testing.T) {
cmdMain := "sh"
cmdArgs := []string{"-c", "echo \"test\""}
extraArgs := []string{"--debug", "--foo", "bar"}
cmds := []PlatformCommand{
{OperatingSystem: "no-os", Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: runtime.GOARCH, Command: "sh", Args: []string{"-c", "echo \"test\""}},
{OperatingSystem: runtime.GOOS, Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: "", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
}
argv := []string{"--debug", "--foo", "bar"}
checkCommand(p, argv, osStrCmp, t)
expectedArgs := append(cmdArgs, extraArgs...)
cmd, args, err := PrepareCommands(cmds, true, extraArgs)
if err != nil {
t.Fatal(err)
}
if cmd != cmdMain {
t.Fatalf("Expected %q, got %q", cmdMain, cmd)
}
if !reflect.DeepEqual(args, expectedArgs) {
t.Fatalf("Expected %v, got %v", expectedArgs, args)
}
}
func TestNoPrepareCommand(t *testing.T) {
p := &Plugin{
Dir: "/tmp", // Unused
Metadata: &Metadata{
Name: "test",
},
func TestPrepareCommandsNoArch(t *testing.T) {
cmdMain := "sh"
cmdArgs := []string{"-c", "echo \"test\""}
cmds := []PlatformCommand{
{OperatingSystem: "no-os", Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: runtime.GOOS, Architecture: "", Command: "sh", Args: []string{"-c", "echo \"test\""}},
{OperatingSystem: runtime.GOOS, Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
}
argv := []string{"--debug", "--foo", "bar"}
_, _, err := p.PrepareCommand(argv)
if err == nil {
t.Fatalf("Expected error to be returned")
cmd, args, err := PrepareCommands(cmds, true, []string{})
if err != nil {
t.Fatal(err)
}
if cmd != cmdMain {
t.Fatalf("Expected %q, got %q", cmdMain, cmd)
}
if !reflect.DeepEqual(args, cmdArgs) {
t.Fatalf("Expected %v, got %v", cmdArgs, args)
}
}
func TestNoMatchPrepareCommand(t *testing.T) {
p := &Plugin{
Dir: "/tmp", // Unused
Metadata: &Metadata{
Name: "test",
PlatformCommand: []PlatformCommand{
{OperatingSystem: "no-os", Architecture: "amd64", Command: "echo -n linux-386"},
},
},
func TestPrepareCommandsNoOsNoArch(t *testing.T) {
cmdMain := "sh"
cmdArgs := []string{"-c", "echo \"test\""}
cmds := []PlatformCommand{
{OperatingSystem: "no-os", Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
{OperatingSystem: "", Architecture: "", Command: "sh", Args: []string{"-c", "echo \"test\""}},
{OperatingSystem: runtime.GOOS, Architecture: "no-arch", Command: "pwsh", Args: []string{"-c", "echo \"error\""}},
}
argv := []string{"--debug", "--foo", "bar"}
if _, _, err := p.PrepareCommand(argv); err == nil {
cmd, args, err := PrepareCommands(cmds, true, []string{})
if err != nil {
t.Fatal(err)
}
if cmd != cmdMain {
t.Fatalf("Expected %q, got %q", cmdMain, cmd)
}
if !reflect.DeepEqual(args, cmdArgs) {
t.Fatalf("Expected %v, got %v", cmdArgs, args)
}
}
func TestPrepareCommandsNoMatch(t *testing.T) {
cmds := []PlatformCommand{
{OperatingSystem: "no-os", Architecture: "no-arch", Command: "sh", Args: []string{"-c", "echo \"test\""}},
{OperatingSystem: runtime.GOOS, Architecture: "no-arch", Command: "sh", Args: []string{"-c", "echo \"test\""}},
{OperatingSystem: "no-os", Architecture: runtime.GOARCH, Command: "sh", Args: []string{"-c", "echo \"test\""}},
}
if _, _, err := PrepareCommands(cmds, true, []string{}); err == nil {
t.Fatalf("Expected error to be returned")
}
}
func TestPrepareCommandsNoCommands(t *testing.T) {
cmds := []PlatformCommand{}
if _, _, err := PrepareCommands(cmds, true, []string{}); err == nil {
t.Fatalf("Expected error to be returned")
}
}
func TestPrepareCommandsExpand(t *testing.T) {
t.Setenv("TEST", "test")
cmdMain := "sh"
cmdArgs := []string{"-c", "echo \"${TEST}\""}
cmds := []PlatformCommand{
{OperatingSystem: "", Architecture: "", Command: cmdMain, Args: cmdArgs},
}
expectedArgs := []string{"-c", "echo \"test\""}
cmd, args, err := PrepareCommands(cmds, true, []string{})
if err != nil {
t.Fatal(err)
}
if cmd != cmdMain {
t.Fatalf("Expected %q, got %q", cmdMain, cmd)
}
if !reflect.DeepEqual(args, expectedArgs) {
t.Fatalf("Expected %v, got %v", expectedArgs, args)
}
}
func TestPrepareCommandsNoExpand(t *testing.T) {
t.Setenv("TEST", "test")
cmdMain := "sh"
cmdArgs := []string{"-c", "echo \"${TEST}\""}
cmds := []PlatformCommand{
{OperatingSystem: "", Architecture: "", Command: cmdMain, Args: cmdArgs},
}
cmd, args, err := PrepareCommands(cmds, false, []string{})
if err != nil {
t.Fatal(err)
}
if cmd != cmdMain {
t.Fatalf("Expected %q, got %q", cmdMain, cmd)
}
if !reflect.DeepEqual(args, cmdArgs) {
t.Fatalf("Expected %v, got %v", cmdArgs, args)
}
}
func TestLoadDir(t *testing.T) {
dirname := "testdata/plugdir/good/hello"
plug, err := LoadDir(dirname)
@ -196,10 +292,16 @@ func TestLoadDir(t *testing.T) {
Version: "0.1.0",
Usage: "usage",
Description: "description",
Command: "$HELM_PLUGIN_DIR/hello.sh",
PlatformCommand: []PlatformCommand{
{OperatingSystem: "linux", Architecture: "", Command: "sh", Args: []string{"-c", "${HELM_PLUGIN_DIR}/hello.sh"}},
{OperatingSystem: "windows", Architecture: "", Command: "pwsh", Args: []string{"-c", "${HELM_PLUGIN_DIR}/hello.ps1"}},
},
IgnoreFlags: true,
Hooks: map[string]string{
Install: "echo installing...",
PlatformHooks: map[string][]PlatformCommand{
Install: {
{OperatingSystem: "linux", Architecture: "", Command: "sh", Args: []string{"-c", "echo \"installing...\""}},
{OperatingSystem: "windows", Architecture: "", Command: "pwsh", Args: []string{"-c", "echo \"installing...\""}},
},
},
}
@ -246,7 +348,6 @@ func TestDownloader(t *testing.T) {
}
func TestLoadAll(t *testing.T) {
// Verify that empty dir loads:
if plugs, err := LoadAll("testdata"); err != nil {
t.Fatalf("error loading dir with no plugins: %s", err)
@ -358,6 +459,30 @@ func TestValidatePluginData(t *testing.T) {
Dir: "no-such-dir",
}
// A mock plugin with no commands
mockNoCommand := mockPlugin("foo")
mockNoCommand.Metadata.PlatformCommand = []PlatformCommand{}
mockNoCommand.Metadata.PlatformHooks = map[string][]PlatformCommand{}
// A mock plugin with legacy commands
mockLegacyCommand := mockPlugin("foo")
mockLegacyCommand.Metadata.PlatformCommand = []PlatformCommand{}
mockLegacyCommand.Metadata.Command = "echo \"mock plugin\""
mockLegacyCommand.Metadata.PlatformHooks = map[string][]PlatformCommand{}
mockLegacyCommand.Metadata.Hooks = map[string]string{
Install: "echo installing...",
}
// A mock plugin with a command also set
mockWithCommand := mockPlugin("foo")
mockWithCommand.Metadata.Command = "echo \"mock plugin\""
// A mock plugin with a hooks also set
mockWithHooks := mockPlugin("foo")
mockWithHooks.Metadata.Hooks = map[string]string{
Install: "echo installing...",
}
for i, item := range []struct {
pass bool
plug *Plugin
@ -369,6 +494,10 @@ func TestValidatePluginData(t *testing.T) {
{false, mockPlugin("foo -bar ")}, // Test trailing chars
{false, mockPlugin("foo\nbar")}, // Test newline
{false, mockMissingMeta}, // Test if the metadata section missing
{true, mockNoCommand}, // Test no command metadata works
{true, mockLegacyCommand}, // Test legacy command metadata works
{false, mockWithCommand}, // Test platformCommand and command both set fails
{false, mockWithHooks}, // Test platformHooks and hooks both set fails
} {
err := validatePluginData(item.plug, fmt.Sprintf("test-%d", i))
if item.pass && err != nil {
@ -400,7 +529,16 @@ func mockPlugin(name string) *Plugin {
Version: "v0.1.2",
Usage: "Mock plugin",
Description: "Mock plugin for testing",
Command: "echo mock plugin",
PlatformCommand: []PlatformCommand{
{OperatingSystem: "linux", Architecture: "", Command: "sh", Args: []string{"-c", "echo \"mock plugin\""}},
{OperatingSystem: "windows", Architecture: "", Command: "pwsh", Args: []string{"-c", "echo \"mock plugin\""}},
},
PlatformHooks: map[string][]PlatformCommand{
Install: {
{OperatingSystem: "linux", Architecture: "", Command: "sh", Args: []string{"-c", "echo \"installing...\""}},
{OperatingSystem: "windows", Architecture: "", Command: "pwsh", Args: []string{"-c", "echo \"installing...\""}},
},
},
},
Dir: "no-such-dir",
}

@ -0,0 +1,3 @@
#!/usr/bin/env pwsh
Write-Host "Hello, world!"

@ -3,7 +3,23 @@ version: "0.1.0"
usage: "usage"
description: |-
description
command: "$HELM_PLUGIN_DIR/hello.sh"
platformCommand:
- os: linux
arch:
command: "sh"
args: ["-c", "${HELM_PLUGIN_DIR}/hello.sh"]
- os: windows
arch:
command: "pwsh"
args: ["-c", "${HELM_PLUGIN_DIR}/hello.ps1"]
ignoreFlags: true
hooks:
install: "echo installing..."
platformHooks:
install:
- os: linux
arch: ""
command: "sh"
args: ["-c", 'echo "installing..."']
- os: windows
arch: ""
command: "pwsh"
args: ["-c", 'echo "installing..."']

@ -90,8 +90,9 @@ func (pusher *OCIPusher) push(chartRef, href string) error {
path.Join(strings.TrimPrefix(href, fmt.Sprintf("%s://", registry.OCIScheme)), meta.Metadata.Name),
meta.Metadata.Version)
chartCreationTime := ctime.Created(stat)
pushOpts = append(pushOpts, registry.PushOptCreationTime(chartCreationTime.Format(time.RFC3339)))
// The time the chart was "created" is semantically the time the chart archive file was last written(modified)
chartArchiveFileCreatedTime := ctime.Modified(stat)
pushOpts = append(pushOpts, registry.PushOptCreationTime(chartArchiveFileCreatedTime.Format(time.RFC3339)))
_, err = client.Push(chartBytes, ref, pushOpts...)
return err

@ -18,6 +18,7 @@ package registry // import "helm.sh/helm/v3/pkg/registry"
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
@ -51,15 +52,24 @@ an underscore (_) in chart version tags when pushing to a registry and back to
a plus (+) when pulling from a registry.`
type (
// RemoteClient shadows the ORAS remote.Client interface
// (hiding the ORAS type from Helm client visibility)
// https://pkg.go.dev/oras.land/oras-go/pkg/registry/remote#Client
RemoteClient interface {
Do(req *http.Request) (*http.Response, error)
}
// Client works with OCI-compliant registries
Client struct {
debug bool
enableCache bool
// path to repository config file e.g. ~/.docker/config.json
credentialsFile string
username string
password string
out io.Writer
authorizer auth.Client
registryAuthorizer *registryauth.Client
registryAuthorizer RemoteClient
resolver func(ref registry.Reference) (remotes.Resolver, error)
httpClient *http.Client
plainHTTP bool
@ -106,6 +116,19 @@ func NewClient(options ...ClientOption) (*Client, error) {
if client.plainHTTP {
opts = append(opts, auth.WithResolverPlainHTTP())
}
// if username and password are set, use them for authentication
// by adding the basic auth Authorization header to the resolver
if client.username != "" && client.password != "" {
concat := client.username + ":" + client.password
encodedAuth := base64.StdEncoding.EncodeToString([]byte(concat))
opts = append(opts, auth.WithResolverHeaders(
http.Header{
"Authorization": []string{"Basic " + encodedAuth},
},
))
}
resolver, err := client.authorizer.ResolverWithOpts(opts...)
if err != nil {
return nil, err
@ -126,6 +149,13 @@ func NewClient(options ...ClientOption) (*Client, error) {
},
Cache: cache,
Credential: func(_ context.Context, reg string) (registryauth.Credential, error) {
if client.username != "" && client.password != "" {
return registryauth.Credential{
Username: client.username,
Password: client.password,
}, nil
}
dockerClient, ok := client.authorizer.(*dockerauth.Client)
if !ok {
return registryauth.EmptyCredential, errors.New("unable to obtain docker client")
@ -169,6 +199,14 @@ func ClientOptEnableCache(enableCache bool) ClientOption {
}
}
// ClientOptBasicAuth returns a function that sets the username and password setting on client options set
func ClientOptBasicAuth(username, password string) ClientOption {
return func(client *Client) {
client.username = username
client.password = password
}
}
// ClientOptWriter returns a function that sets the writer setting on client options set
func ClientOptWriter(out io.Writer) ClientOption {
return func(client *Client) {
@ -176,6 +214,26 @@ func ClientOptWriter(out io.Writer) ClientOption {
}
}
// ClientOptAuthorizer returns a function that sets the authorizer setting on a client options set. This
// can be used to override the default authorization mechanism.
//
// Depending on the use-case you may need to set both ClientOptAuthorizer and ClientOptRegistryAuthorizer.
func ClientOptAuthorizer(authorizer auth.Client) ClientOption {
return func(client *Client) {
client.authorizer = authorizer
}
}
// ClientOptRegistryAuthorizer returns a function that sets the registry authorizer setting on a client options set. This
// can be used to override the default authorization mechanism.
//
// Depending on the use-case you may need to set both ClientOptAuthorizer and ClientOptRegistryAuthorizer.
func ClientOptRegistryAuthorizer(registryAuthorizer RemoteClient) ClientOption {
return func(client *Client) {
client.registryAuthorizer = registryAuthorizer
}
}
// ClientOptCredentialsFile returns a function that sets the credentialsFile setting on a client options set
func ClientOptCredentialsFile(credentialsFile string) ClientOption {
return func(client *Client) {

@ -89,6 +89,7 @@ func setup(suite *TestSuite, tlsEnabled, insecure bool) *registry.Registry {
ClientOptWriter(suite.Out),
ClientOptCredentialsFile(credentialsFile),
ClientOptResolver(nil),
ClientOptBasicAuth(testUsername, testPassword),
}
if tlsEnabled {
@ -128,11 +129,12 @@ func setup(suite *TestSuite, tlsEnabled, insecure bool) *registry.Registry {
// This is required because Docker enforces HTTP if the registry
// host is localhost/127.0.0.1.
suite.DockerRegistryHost = fmt.Sprintf("helm-test-registry:%d", port)
suite.srv, _ = mockdns.NewServer(map[string]mockdns.Zone{
suite.srv, err = mockdns.NewServer(map[string]mockdns.Zone{
"helm-test-registry.": {
A: []string{"127.0.0.1"},
},
}, false)
suite.Nil(err, "no error creating mock DNS server")
suite.srv.PatchNet(net.DefaultResolver)
config.HTTP.Addr = fmt.Sprintf(":%d", port)
@ -349,7 +351,7 @@ func testPull(suite *TestSuite) {
// full pull with chart and prov
result, err := suite.RegistryClient.Pull(ref, PullOptWithProv(true))
suite.Nil(err, "no error pulling a chart with prov")
suite.Require().Nil(err, "no error pulling a chart with prov")
// Validate the output
// Note: these digests/sizes etc may change if the test chart/prov files are modified,

@ -74,6 +74,24 @@ func Mock(opts *MockReleaseOptions) *Release {
Name: "foo",
Version: "0.1.0-beta.1",
AppVersion: "1.0",
Annotations: map[string]string{
"category": "web-apps",
"supported": "true",
},
Dependencies: []*chart.Dependency{
{
Name: "cool-plugin",
Version: "1.0.0",
Repository: "https://coolplugin.io/charts",
Condition: "coolPlugin.enabled",
Enabled: true,
},
{
Name: "crds",
Version: "2.7.1",
Condition: "crds.enabled",
},
},
},
Templates: []*chart.File{
{Name: "templates/foo.tpl", Data: []byte(MockManifest)},

@ -21,5 +21,9 @@ import (
)
func Created(fi os.FileInfo) time.Time {
return created(fi)
return modified(fi)
}
func Modified(fi os.FileInfo) time.Time {
return modified(fi)
}

@ -23,8 +23,8 @@ import (
"time"
)
func created(fi os.FileInfo) time.Time {
func modified(fi os.FileInfo) time.Time {
st := fi.Sys().(*syscall.Stat_t)
//nolint
return time.Unix(int64(st.Ctim.Sec), int64(st.Ctim.Nsec))
return time.Unix(int64(st.Mtim.Sec), int64(st.Mtim.Nsec))
}

@ -22,6 +22,6 @@ import (
"time"
)
func created(fi os.FileInfo) time.Time {
func modified(fi os.FileInfo) time.Time {
return fi.ModTime()
}

@ -30,6 +30,7 @@ HAS_WGET="$(type "wget" &> /dev/null && echo true || echo false)"
HAS_OPENSSL="$(type "openssl" &> /dev/null && echo true || echo false)"
HAS_GPG="$(type "gpg" &> /dev/null && echo true || echo false)"
HAS_GIT="$(type "git" &> /dev/null && echo true || echo false)"
HAS_TAR="$(type "tar" &> /dev/null && echo true || echo false)"
# initArch discovers the architecture for this system.
initArch() {
@ -102,6 +103,11 @@ verifySupported() {
if [ "${HAS_GIT}" != "true" ]; then
echo "[WARNING] Could not find git. It is required for plugin installation."
fi
if [ "${HAS_TAR}" != "true" ]; then
echo "[ERROR] Could not find tar. It is required to extract the helm binary archive."
exit 1
fi
}
# checkDesiredVersion checks if the desired version is available.

Loading…
Cancel
Save