chore(oci): migrate to ORAS Golang library v2

Updates from `oras.land/oras-go` to `oras.land/oras-go/v2`. The main
user facing change is that the `--plain-http` parameter is now required
even when accessing OCI registries running on `localhost`.

I'm new to this codebase and some tests needed to be adjusted to match
the new dependency, please look over the test changes to see if there
are any issues with those.

Fixes #11821

Signed-off-by: Zoran Regvart <zoran@regvart.com>
pull/12310/head
Zoran Regvart 11 months ago
parent 21568dee29
commit 156e562f38
No known key found for this signature in database
GPG Key ID: 6D9197EFE5AB7D75

@ -65,10 +65,17 @@ func newDependencyBuildCmd(cfg *action.Configuration, out io.Writer) *cobra.Comm
RepositoryCache: settings.RepositoryCache, RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug, Debug: settings.Debug,
} }
registryClient, err := newDefaultRegistryClient(client.PlainHTTP)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
man.RegistryClient = registryClient
if client.Verify { if client.Verify {
man.Verify = downloader.VerifyIfPossible man.Verify = downloader.VerifyIfPossible
} }
err := man.Build() err = man.Build()
if e, ok := err.(downloader.ErrRepoNotFound); ok { if e, ok := err.(downloader.ErrRepoNotFound); ok {
return fmt.Errorf("%s. Please add the missing repos via 'helm repo add'", e.Error()) return fmt.Errorf("%s. Please add the missing repos via 'helm repo add'", e.Error())
} }
@ -80,6 +87,7 @@ func newDependencyBuildCmd(cfg *action.Configuration, out io.Writer) *cobra.Comm
f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures") f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures")
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys") 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.BoolVar(&client.SkipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
f.BoolVar(&client.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download")
return cmd return cmd
} }

@ -58,7 +58,7 @@ func TestDependencyBuildCmd(t *testing.T) {
createTestingChart(t, rootDir, chartname, srv.URL()) createTestingChart(t, rootDir, chartname, srv.URL())
repoFile := filepath.Join(rootDir, "repositories.yaml") repoFile := filepath.Join(rootDir, "repositories.yaml")
cmd := fmt.Sprintf("dependency build '%s' --repository-config %s --repository-cache %s", filepath.Join(rootDir, chartname), repoFile, rootDir) cmd := fmt.Sprintf("dependency build '%s' --repository-config %s --repository-cache %s --plain-http", filepath.Join(rootDir, chartname), repoFile, rootDir)
_, out, err := executeActionCommand(cmd) _, out, err := executeActionCommand(cmd)
// In the first pass, we basically want the same results as an update. // In the first pass, we basically want the same results as an update.
@ -117,7 +117,7 @@ func TestDependencyBuildCmd(t *testing.T) {
t.Errorf("mismatched versions. Expected %q, got %q", "0.1.0", v) t.Errorf("mismatched versions. Expected %q, got %q", "0.1.0", v)
} }
skipRefreshCmd := fmt.Sprintf("dependency build '%s' --skip-refresh --repository-config %s --repository-cache %s", filepath.Join(rootDir, chartname), repoFile, rootDir) skipRefreshCmd := fmt.Sprintf("dependency build '%s' --skip-refresh --repository-config %s --repository-cache %s --plain-http", filepath.Join(rootDir, chartname), repoFile, rootDir)
_, out, err = executeActionCommand(skipRefreshCmd) _, out, err = executeActionCommand(skipRefreshCmd)
// In this pass, we check --skip-refresh option becomes effective. // In this pass, we check --skip-refresh option becomes effective.
@ -134,7 +134,7 @@ func TestDependencyBuildCmd(t *testing.T) {
if err := chartutil.SaveDir(c, dir()); err != nil { if err := chartutil.SaveDir(c, dir()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
cmd = fmt.Sprintf("dependency build '%s' --repository-config %s --repository-cache %s --registry-config %s/config.json", cmd = fmt.Sprintf("dependency build '%s' --repository-config %s --repository-cache %s --registry-config %s/config.json --plain-http",
dir(ociChartName), dir(ociChartName),
dir("repositories.yaml"), dir("repositories.yaml"),
dir(), dir(),

@ -16,6 +16,7 @@ limitations under the License.
package main package main
import ( import (
"fmt"
"io" "io"
"path/filepath" "path/filepath"
@ -68,6 +69,13 @@ func newDependencyUpdateCmd(cfg *action.Configuration, out io.Writer) *cobra.Com
RepositoryCache: settings.RepositoryCache, RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug, Debug: settings.Debug,
} }
registryClient, err := newDefaultRegistryClient(client.PlainHTTP)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
man.RegistryClient = registryClient
if client.Verify { if client.Verify {
man.Verify = downloader.VerifyAlways man.Verify = downloader.VerifyAlways
} }
@ -79,6 +87,7 @@ func newDependencyUpdateCmd(cfg *action.Configuration, out io.Writer) *cobra.Com
f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures") f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures")
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys") 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.BoolVar(&client.SkipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
f.BoolVar(&client.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download")
return cmd return cmd
} }

@ -67,7 +67,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
} }
_, out, err := executeActionCommand( _, out, err := executeActionCommand(
fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir()), fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir()),
) )
if err != nil { if err != nil {
t.Logf("Output: %s", out) t.Logf("Output: %s", out)
@ -110,7 +110,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
_, out, err = executeActionCommand(fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir())) _, out, err = executeActionCommand(fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir()))
if err != nil { if err != nil {
t.Logf("Output: %s", out) t.Logf("Output: %s", out)
t.Fatal(err) t.Fatal(err)
@ -131,7 +131,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
if err := chartutil.SaveDir(c, dir()); err != nil { if err := chartutil.SaveDir(c, dir()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
cmd := fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --registry-config %s/config.json", cmd := fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --registry-config %s/config.json --plain-http",
dir(ociChartName), dir(ociChartName),
dir("repositories.yaml"), dir("repositories.yaml"),
dir(), dir(),
@ -169,7 +169,7 @@ func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
} }
createTestingChart(t, dir(), chartname, srv.URL()) createTestingChart(t, dir(), chartname, srv.URL())
_, output, err := executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir())) _, output, err := executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir()))
if err != nil { if err != nil {
t.Logf("Output: %s", output) t.Logf("Output: %s", output)
t.Fatal(err) t.Fatal(err)
@ -178,7 +178,7 @@ func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
// Chart repo is down // Chart repo is down
srv.Stop() srv.Stop()
_, output, err = executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir())) _, output, err = executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir()))
if err == nil { if err == nil {
t.Logf("Output: %s", output) t.Logf("Output: %s", output)
t.Fatal("Expected error, got nil") t.Fatal("Expected error, got nil")

@ -198,7 +198,7 @@ func TestPullCmd(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
outdir := srv.Root() outdir := srv.Root()
cmd := fmt.Sprintf("fetch %s -d '%s' --repository-config %s --repository-cache %s --registry-config %s", cmd := fmt.Sprintf("fetch %s -d '%s' --repository-config %s --repository-cache %s --registry-config %s --plain-http",
tt.args, tt.args,
outdir, outdir,
filepath.Join(outdir, "repositories.yaml"), filepath.Join(outdir, "repositories.yaml"),

@ -43,6 +43,7 @@ type registryLoginOptions struct {
keyFile string keyFile string
caFile string caFile string
insecure bool insecure bool
plainHTTP bool
} }
func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
@ -66,7 +67,8 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
action.WithCertFile(o.certFile), action.WithCertFile(o.certFile),
action.WithKeyFile(o.keyFile), action.WithKeyFile(o.keyFile),
action.WithCAFile(o.caFile), action.WithCAFile(o.caFile),
action.WithInsecure(o.insecure)) action.WithInsecure(o.insecure),
action.WithPlainHTTPLogin(o.plainHTTP))
}, },
} }
@ -78,6 +80,7 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
f.StringVar(&o.certFile, "cert-file", "", "identify registry client using this SSL certificate file") f.StringVar(&o.certFile, "cert-file", "", "identify registry client using this SSL certificate file")
f.StringVar(&o.keyFile, "key-file", "", "identify registry client using this SSL key file") f.StringVar(&o.keyFile, "key-file", "", "identify registry client using this SSL key file")
f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&o.plainHTTP, "plain-http", false, "use insecure HTTP connections for the chart upload")
return cmd return cmd
} }

@ -11,7 +11,6 @@ require (
github.com/Masterminds/squirrel v1.5.4 github.com/Masterminds/squirrel v1.5.4
github.com/Masterminds/vcs v1.13.3 github.com/Masterminds/vcs v1.13.3
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/containerd/containerd v1.7.23
github.com/cyphar/filepath-securejoin v0.3.4 github.com/cyphar/filepath-securejoin v0.3.4
github.com/distribution/distribution/v3 v3.0.0-rc.1 github.com/distribution/distribution/v3 v3.0.0-rc.1
github.com/evanphx/json-patch v5.9.0+incompatible github.com/evanphx/json-patch v5.9.0+incompatible
@ -29,7 +28,6 @@ require (
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/rubenv/sql-migrate v1.7.0 github.com/rubenv/sql-migrate v1.7.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
@ -45,7 +43,7 @@ require (
k8s.io/client-go v0.31.2 k8s.io/client-go v0.31.2
k8s.io/klog/v2 v2.130.1 k8s.io/klog/v2 v2.130.1
k8s.io/kubectl v0.31.2 k8s.io/kubectl v0.31.2
oras.land/oras-go v1.2.5 oras.land/oras-go/v2 v2.5.0
sigs.k8s.io/yaml v1.4.0 sigs.k8s.io/yaml v1.4.0
) )
@ -60,19 +58,12 @@ require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // 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/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.6.0 // 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.8.2 // 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-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-metrics v0.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect
@ -119,7 +110,6 @@ require (
github.com/miekg/dns v1.1.57 // indirect github.com/miekg/dns v1.1.57 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.4.0 // indirect github.com/moby/spdystream v0.4.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
@ -138,6 +128,7 @@ require (
github.com/redis/go-redis/v9 v9.1.0 // indirect github.com/redis/go-redis/v9 v9.1.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/cast v1.7.0 // indirect github.com/spf13/cast v1.7.0 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect

@ -24,10 +24,6 @@ github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Masterminds/vcs v1.13.3 h1:IIA2aBdXvfbIM+yl/eTnL4hb1XwdpvuQLglAix1gweE= github.com/Masterminds/vcs v1.13.3 h1:IIA2aBdXvfbIM+yl/eTnL4hb1XwdpvuQLglAix1gweE=
github.com/Masterminds/vcs v1.13.3/go.mod h1:TiE7xuEjl1N4j016moRd6vezp6e6Lz23gypeXfzXeW8= github.com/Masterminds/vcs v1.13.3/go.mod h1:TiE7xuEjl1N4j016moRd6vezp6e6Lz23gypeXfzXeW8=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
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/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 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/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= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@ -59,18 +55,6 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 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/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=
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
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 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 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 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
@ -90,22 +74,12 @@ github.com/distribution/distribution/v3 v3.0.0-rc.1 h1:6M4ewmPBUhF7wtQ8URLOQ1W/P
github.com/distribution/distribution/v3 v3.0.0-rc.1/go.mod h1:tFjaPDeHCrLg28e4feBIy27cP+qmrc/mvkl6MFIfVi4= 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 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 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=
github.com/docker/cli v25.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
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.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= 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/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= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -157,8 +131,6 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -278,14 +250,8 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -395,8 +361,6 @@ 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.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
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/bridges/prometheus v0.54.0 h1:WWL67oxtknNVMb70lJXxXruf8UyK/a9hmIE1XO3Uedg= 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/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 h1:dTmcmVm4J54IRPGm5oVjLci1uYat4UDea84E2tyBaAk=
@ -602,8 +566,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/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.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
@ -628,8 +590,8 @@ k8s.io/kubectl v0.31.2 h1:gTxbvRkMBwvTSAlobiTVqsH6S8Aa1aGyBcu5xYLsn8M=
k8s.io/kubectl v0.31.2/go.mod h1:EyASYVU6PY+032RrTh5ahtSOMgoDRIux9V1JLKtG5xM= k8s.io/kubectl v0.31.2/go.mod h1:EyASYVU6PY+032RrTh5ahtSOMgoDRIux9V1JLKtG5xM=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c=
oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g=

@ -38,6 +38,7 @@ type Dependency struct {
Keyring string Keyring string
SkipRefresh bool SkipRefresh bool
ColumnWidth uint ColumnWidth uint
PlainHTTP bool
} }
// NewDependency creates a new Dependency object with the given configuration. // NewDependency creates a new Dependency object with the given configuration.

@ -24,11 +24,12 @@ import (
// RegistryLogin performs a registry login operation. // RegistryLogin performs a registry login operation.
type RegistryLogin struct { type RegistryLogin struct {
cfg *Configuration cfg *Configuration
certFile string certFile string
keyFile string keyFile string
caFile string caFile string
insecure bool insecure bool
plainHTTP bool
} }
type RegistryLoginOpt func(*RegistryLogin) error type RegistryLoginOpt func(*RegistryLogin) error
@ -65,6 +66,13 @@ func WithCAFile(caFile string) RegistryLoginOpt {
} }
} }
func WithPlainHTTPLogin(isPlain bool) RegistryLoginOpt {
return func(r *RegistryLogin) error {
r.plainHTTP = isPlain
return nil
}
}
// NewRegistryLogin creates a new RegistryLogin object with the given configuration. // NewRegistryLogin creates a new RegistryLogin object with the given configuration.
func NewRegistryLogin(cfg *Configuration) *RegistryLogin { func NewRegistryLogin(cfg *Configuration) *RegistryLogin {
return &RegistryLogin{ return &RegistryLogin{
@ -84,5 +92,6 @@ func (a *RegistryLogin) Run(_ io.Writer, hostname string, username string, passw
hostname, hostname,
registry.LoginOptBasicAuth(username, password), registry.LoginOptBasicAuth(username, password),
registry.LoginOptInsecure(a.insecure), registry.LoginOptInsecure(a.insecure),
registry.LoginOptTLSClientConfig(a.certFile, a.keyFile, a.caFile)) registry.LoginOptTLSClientConfig(a.certFile, a.keyFile, a.caFile),
registry.LoginOptPlainText(a.plainHTTP))
} }

@ -18,24 +18,29 @@ package registry // import "helm.sh/helm/v3/pkg/registry"
import ( import (
"context" "context"
"crypto/tls"
"crypto/x509"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"os"
"sort" "sort"
"strings" "strings"
"sync"
"github.com/Masterminds/semver/v3" "github.com/Masterminds/semver/v3"
"github.com/containerd/containerd/remotes" "github.com/opencontainers/image-spec/specs-go"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"oras.land/oras-go/pkg/auth" "oras.land/oras-go/v2"
dockerauth "oras.land/oras-go/pkg/auth/docker" "oras.land/oras-go/v2/content"
"oras.land/oras-go/pkg/content" "oras.land/oras-go/v2/content/memory"
"oras.land/oras-go/pkg/oras" "oras.land/oras-go/v2/registry"
"oras.land/oras-go/pkg/registry" "oras.land/oras-go/v2/registry/remote"
registryremote "oras.land/oras-go/pkg/registry/remote" "oras.land/oras-go/v2/registry/remote/auth"
registryauth "oras.land/oras-go/pkg/registry/remote/auth" "oras.land/oras-go/v2/registry/remote/credentials"
"oras.land/oras-go/v2/registry/remote/retry"
"helm.sh/helm/v3/internal/version" "helm.sh/helm/v3/internal/version"
"helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart"
@ -55,13 +60,12 @@ type (
debug bool debug bool
enableCache bool enableCache bool
// path to repository config file e.g. ~/.docker/config.json // path to repository config file e.g. ~/.docker/config.json
credentialsFile string credentialsFile string
out io.Writer out io.Writer
authorizer auth.Client authorizer *auth.Client
registryAuthorizer *registryauth.Client credentialsStore credentials.Store
resolver func(ref registry.Reference) (remotes.Resolver, error) httpClient *http.Client
httpClient *http.Client plainHTTP bool
plainHTTP bool
} }
// ClientOption allows specifying various settings configurable by the user for overriding the defaults // ClientOption allows specifying various settings configurable by the user for overriding the defaults
@ -80,77 +84,62 @@ func NewClient(options ...ClientOption) (*Client, error) {
if client.credentialsFile == "" { if client.credentialsFile == "" {
client.credentialsFile = helmpath.ConfigPath(CredentialsFileBasename) client.credentialsFile = helmpath.ConfigPath(CredentialsFileBasename)
} }
if client.authorizer == nil { if client.httpClient == nil {
authClient, err := dockerauth.NewClientWithDockerFallback(client.credentialsFile) type cloner[T any] interface {
if err != nil { Clone() T
return nil, err
} }
client.authorizer = authClient
}
resolverFn := client.resolver // copy for avoiding recursive call // try to copy (clone) the http.DefaultTransport so any mutations we
client.resolver = func(ref registry.Reference) (remotes.Resolver, error) { // perform on it (e.g. TLS config) are not reflected globally
if resolverFn != nil { // follow https://github.com/golang/go/issues/39299 for a more elegant
// validate if the resolverFn returns a valid resolver // solution in the future
if resolver, err := resolverFn(ref); resolver != nil && err == nil { transport := http.DefaultTransport
return resolver, nil if t, ok := transport.(cloner[*http.Transport]); ok {
} transport = t.Clone()
} else if t, ok := transport.(cloner[http.RoundTripper]); ok {
// this branch will not be used with go 1.20, it was added
// optimistically to try to clone if the http.DefaultTransport
// implementation changes, still the Clone method in that case
// might not return http.RoundTripper...
transport = t.Clone()
} }
headers := http.Header{}
headers.Set("User-Agent", version.GetUserAgent()) client.httpClient = &http.Client{
opts := []auth.ResolverOption{auth.WithResolverHeaders(headers)} Transport: retry.NewTransport(transport),
if client.httpClient != nil {
opts = append(opts, auth.WithResolverClient(client.httpClient))
} }
if client.plainHTTP { }
opts = append(opts, auth.WithResolverPlainHTTP()) if client.authorizer == nil {
authorizer := auth.Client{
Client: client.httpClient,
}
authorizer.SetUserAgent(version.GetUserAgent())
storeOptions := credentials.StoreOptions{
AllowPlaintextPut: true,
DetectDefaultNativeStore: true,
} }
resolver, err := client.authorizer.ResolverWithOpts(opts...) store, err := credentials.NewStore(client.credentialsFile, storeOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resolver, nil dockerStore, err := credentials.NewStoreFromDocker(storeOptions)
} if err != nil {
// should only fail if user home directory can't be determined
// allocate a cache if option is set client.credentialsStore = store
var cache registryauth.Cache } else {
if client.enableCache { // use Helm credentials with fallback to Docker
cache = registryauth.DefaultCache client.credentialsStore = credentials.NewStoreWithFallbacks(store, dockerStore)
} }
if client.registryAuthorizer == nil {
client.registryAuthorizer = &registryauth.Client{
Client: client.httpClient,
Header: http.Header{
"User-Agent": {version.GetUserAgent()},
},
Cache: cache,
Credential: func(_ context.Context, reg string) (registryauth.Credential, error) {
dockerClient, ok := client.authorizer.(*dockerauth.Client)
if !ok {
return registryauth.EmptyCredential, errors.New("unable to obtain docker client")
}
username, password, err := dockerClient.Credential(reg)
if err != nil {
return registryauth.EmptyCredential, errors.New("unable to retrieve credentials")
}
// A blank returned username and password value is a bearer token
if username == "" && password != "" {
return registryauth.Credential{
RefreshToken: password,
}, nil
}
return registryauth.Credential{ authorizer.Credential = credentials.Credential(client.credentialsStore)
Username: username,
Password: password,
}, nil
}, if client.enableCache {
authorizer.Cache = auth.NewCache()
} }
client.authorizer = &authorizer
} }
return client, nil return client, nil
} }
@ -195,74 +184,134 @@ func ClientOptPlainHTTP() ClientOption {
} }
} }
// ClientOptResolver returns a function that sets the resolver setting on a client options set
func ClientOptResolver(resolver remotes.Resolver) ClientOption {
return func(client *Client) {
client.resolver = func(_ registry.Reference) (remotes.Resolver, error) {
return resolver, nil
}
}
}
type ( type (
// LoginOption allows specifying various settings on login // LoginOption allows specifying various settings on login
LoginOption func(*loginOperation) LoginOption func(host string, client *Client) error
loginOperation struct {
username string
password string
insecure bool
certFile string
keyFile string
caFile string
}
) )
// Login logs into a registry // Login logs into a registry
func (c *Client) Login(host string, options ...LoginOption) error { func (c *Client) Login(host string, options ...LoginOption) error {
operation := &loginOperation{}
for _, option := range options { for _, option := range options {
option(operation) if err := option(host, c); err != nil {
return fmt.Errorf("configuring login option: %w", err)
}
} }
authorizerLoginOpts := []auth.LoginOption{
auth.WithLoginContext(ctx(c.out, c.debug)), reg, err := remote.NewRegistry(host)
auth.WithLoginHostname(host), if err != nil {
auth.WithLoginUsername(operation.username), return err
auth.WithLoginSecret(operation.password),
auth.WithLoginUserAgent(version.GetUserAgent()),
auth.WithLoginTLS(operation.certFile, operation.keyFile, operation.caFile),
} }
if operation.insecure { reg.PlainHTTP = c.plainHTTP
authorizerLoginOpts = append(authorizerLoginOpts, auth.WithLoginInsecure()) reg.Client = c.authorizer
ctx := context.Background()
cred, err := c.authorizer.Credential(ctx, host)
if err != nil {
return fmt.Errorf("fetching credentials for %q: %w", host, err)
} }
if err := c.authorizer.LoginWithOpts(authorizerLoginOpts...); err != nil {
if err := reg.Ping(ctx); err != nil {
return fmt.Errorf("authenticating to %q: %w", host, err)
}
key := credentials.ServerAddressFromRegistry(host)
if err := c.credentialsStore.Put(ctx, key, cred); err != nil {
return err return err
} }
fmt.Fprintln(c.out, "Login Succeeded") fmt.Fprintln(c.out, "Login Succeeded")
return nil return nil
} }
// LoginOptBasicAuth returns a function that sets the username/password settings on login // LoginOptBasicAuth returns a function that sets the username/password settings on login
func LoginOptBasicAuth(username string, password string) LoginOption { func LoginOptBasicAuth(username string, password string) LoginOption {
return func(operation *loginOperation) { return func(host string, client *Client) error {
operation.username = username client.authorizer.Credential = auth.StaticCredential(host, auth.Credential{Username: username, Password: password})
operation.password = password return nil
}
}
// LoginOptBasicAuth returns a function that allows plaintext (HTTP) login
func LoginOptPlainText(isPlainText bool) LoginOption {
return func(host string, client *Client) error {
client.plainHTTP = isPlainText
return nil
}
}
func ensureTLSConfig(client *auth.Client) (*tls.Config, error) {
var transport *http.Transport
switch t := client.Client.Transport.(type) {
case *http.Transport:
transport = t
case *retry.Transport:
switch t := t.Base.(type) {
case *http.Transport:
transport = t
}
}
if transport == nil {
// we don't know how to access the http.Transport, most likely the
// auth.Client.Client was provided by API user
return nil, fmt.Errorf("unable to access TLS client configuration, the provided HTTP Transport is not supported, given: %T", client.Client.Transport)
} }
if transport.TLSClientConfig == nil {
transport.TLSClientConfig = &tls.Config{}
}
return transport.TLSClientConfig, nil
} }
// LoginOptInsecure returns a function that sets the insecure setting on login // LoginOptInsecure returns a function that sets the insecure setting on login
func LoginOptInsecure(insecure bool) LoginOption { func LoginOptInsecure(insecure bool) LoginOption {
return func(operation *loginOperation) { return func(_ string, client *Client) error {
operation.insecure = insecure tlsConfig, err := ensureTLSConfig(client.authorizer)
if err != nil {
return err
}
tlsConfig.InsecureSkipVerify = insecure
return nil
} }
} }
// LoginOptTLSClientConfig returns a function that sets the TLS settings on login. // LoginOptTLSClientConfig returns a function that sets the TLS settings on login.
func LoginOptTLSClientConfig(certFile, keyFile, caFile string) LoginOption { func LoginOptTLSClientConfig(certFile, keyFile, caFile string) LoginOption {
return func(operation *loginOperation) { return func(_ string, client *Client) error {
operation.certFile = certFile if (certFile == "" || keyFile == "") && caFile == "" {
operation.keyFile = keyFile return nil
operation.caFile = caFile }
tlsConfig, err := ensureTLSConfig(client.authorizer)
if err != nil {
return err
}
if certFile != "" && keyFile != "" {
authCert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return err
}
tlsConfig.Certificates = []tls.Certificate{authCert}
}
if caFile != "" {
certPool := x509.NewCertPool()
ca, err := os.ReadFile(caFile)
if err != nil {
return err
}
if !certPool.AppendCertsFromPEM(ca) {
return fmt.Errorf("unable to parse CA file: %q", caFile)
}
tlsConfig.RootCAs = certPool
}
return nil
} }
} }
@ -279,7 +328,8 @@ func (c *Client) Logout(host string, opts ...LogoutOption) error {
for _, opt := range opts { for _, opt := range opts {
opt(operation) opt(operation)
} }
if err := c.authorizer.Logout(ctx(c.out, c.debug), host); err != nil {
if err := credentials.Logout(context.Background(), c.credentialsStore, host); err != nil {
return err return err
} }
fmt.Fprintf(c.out, "Removing login credentials for %s\n", host) fmt.Fprintf(c.out, "Removing login credentials for %s\n", host)
@ -334,8 +384,9 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
return nil, errors.New( return nil, errors.New(
"must specify at least one layer to pull (chart/prov)") "must specify at least one layer to pull (chart/prov)")
} }
memoryStore := content.NewMemory() memoryStore := memory.New()
allowedMediaTypes := []string{ allowedMediaTypes := []string{
ocispec.MediaTypeImageManifest,
ConfigMediaType, ConfigMediaType,
} }
minNumDescriptors := 1 // 1 for the config minNumDescriptors := 1 // 1 for the config
@ -351,18 +402,35 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
} }
var descriptors, layers []ocispec.Descriptor var descriptors, layers []ocispec.Descriptor
remotesResolver, err := c.resolver(parsedRef) descriptorsMutex := sync.Mutex{}
repository, err := remote.NewRepository(parsedRef.String())
if err != nil { if err != nil {
return nil, err return nil, err
} }
registryStore := content.Registry{Resolver: remotesResolver} repository.PlainHTTP = c.plainHTTP
repository.Client = c.authorizer
ctx := context.Background()
sort.Strings(allowedMediaTypes)
manifest, err := oras.Copy(ctx, repository, parsedRef.String(), memoryStore, "", oras.CopyOptions{
CopyGraphOptions: oras.CopyGraphOptions{
PreCopy: func(ctx context.Context, desc ocispec.Descriptor) error {
mediaType := desc.MediaType
if i := sort.SearchStrings(allowedMediaTypes, mediaType); i >= len(allowedMediaTypes) || allowedMediaTypes[i] != mediaType {
return errors.Errorf("media type %q is not allowed, found in descriptor with digest: %q", mediaType, desc.Digest)
}
descriptorsMutex.Lock()
defer descriptorsMutex.Unlock()
layers = append(layers, desc)
manifest, err := oras.Copy(ctx(c.out, c.debug), registryStore, parsedRef.String(), memoryStore, "", return nil
oras.WithPullEmptyNameAllowed(), },
oras.WithAllowedMediaTypes(allowedMediaTypes), },
oras.WithLayerDescriptors(func(l []ocispec.Descriptor) { })
layers = l
}))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -421,54 +489,37 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) {
Prov: &DescriptorPullSummary{}, Prov: &DescriptorPullSummary{},
Ref: parsedRef.String(), Ref: parsedRef.String(),
} }
var getManifestErr error
if _, manifestData, ok := memoryStore.Get(manifest); !ok { result.Manifest.Data, err = content.FetchAll(ctx, memoryStore, manifest)
getManifestErr = errors.Errorf("Unable to retrieve blob with digest %s", manifest.Digest) if err != nil {
} else { return nil, fmt.Errorf("unable to retrieve blob with digest %s: %w", manifest.Digest, err)
result.Manifest.Data = manifestData
}
if getManifestErr != nil {
return nil, getManifestErr
}
var getConfigDescriptorErr error
if _, configData, ok := memoryStore.Get(*configDescriptor); !ok {
getConfigDescriptorErr = errors.Errorf("Unable to retrieve blob with digest %s", configDescriptor.Digest)
} else {
result.Config.Data = configData
var meta *chart.Metadata
if err := json.Unmarshal(configData, &meta); err != nil {
return nil, err
}
result.Chart.Meta = meta
} }
if getConfigDescriptorErr != nil {
return nil, getConfigDescriptorErr result.Config.Data, err = content.FetchAll(ctx, memoryStore, *configDescriptor)
if err != nil {
return nil, fmt.Errorf("unable to retrieve blob with digest %s: %w", configDescriptor.Digest, err)
}
if err := json.Unmarshal(result.Config.Data, &result.Chart.Meta); err != nil {
return nil, err
} }
if operation.withChart { if operation.withChart {
var getChartDescriptorErr error result.Chart.Data, err = content.FetchAll(ctx, memoryStore, *chartDescriptor)
if _, chartData, ok := memoryStore.Get(*chartDescriptor); !ok { if err != nil {
getChartDescriptorErr = errors.Errorf("Unable to retrieve blob with digest %s", chartDescriptor.Digest) return nil, fmt.Errorf("unable to retrieve blob with digest %s: %w", chartDescriptor.Digest, err)
} else {
result.Chart.Data = chartData
result.Chart.Digest = chartDescriptor.Digest.String()
result.Chart.Size = chartDescriptor.Size
}
if getChartDescriptorErr != nil {
return nil, getChartDescriptorErr
} }
result.Chart.Digest = chartDescriptor.Digest.String()
result.Chart.Size = chartDescriptor.Size
} }
if operation.withProv && !provMissing { if operation.withProv && !provMissing {
var getProvDescriptorErr error result.Prov.Data, err = content.FetchAll(ctx, memoryStore, *provDescriptor)
if _, provData, ok := memoryStore.Get(*provDescriptor); !ok { if err != nil {
getProvDescriptorErr = errors.Errorf("Unable to retrieve blob with digest %s", provDescriptor.Digest) return nil, fmt.Errorf("unable to retrieve blob with digest %s: %w", provDescriptor.Digest, err)
} else {
result.Prov.Data = provData
result.Prov.Digest = provDescriptor.Digest.String()
result.Prov.Size = provDescriptor.Size
}
if getProvDescriptorErr != nil {
return nil, getProvDescriptorErr
} }
result.Prov.Digest = provDescriptor.Digest.String()
result.Prov.Size = provDescriptor.Size
} }
fmt.Fprintf(c.out, "Pulled: %s\n", result.Ref) fmt.Fprintf(c.out, "Pulled: %s\n", result.Ref)
@ -556,8 +607,11 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu
"strict mode enabled, ref basename and tag must match the chart name and version") "strict mode enabled, ref basename and tag must match the chart name and version")
} }
} }
memoryStore := content.NewMemory()
chartDescriptor, err := memoryStore.Add("", ChartLayerMediaType, data) ctx := context.Background()
memoryStore := memory.New()
chartDescriptor, err := oras.PushBytes(ctx, memoryStore, ChartLayerMediaType, data)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -567,43 +621,57 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu
return nil, err return nil, err
} }
configDescriptor, err := memoryStore.Add("", ConfigMediaType, configData) configDescriptor, err := oras.PushBytes(ctx, memoryStore, ConfigMediaType, configData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
descriptors := []ocispec.Descriptor{chartDescriptor} layers := []ocispec.Descriptor{chartDescriptor}
var provDescriptor ocispec.Descriptor var provDescriptor ocispec.Descriptor
if operation.provData != nil { if operation.provData != nil {
provDescriptor, err = memoryStore.Add("", ProvLayerMediaType, operation.provData) provDescriptor, err = oras.PushBytes(ctx, memoryStore, ProvLayerMediaType, operation.provData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
descriptors = append(descriptors, provDescriptor) layers = append(layers, provDescriptor)
} }
// sort layers for determinism, similar to how ORAS v1 does it
sort.Slice(layers, func(i, j int) bool {
return layers[i].Digest < layers[j].Digest
})
ociAnnotations := generateOCIAnnotations(meta, operation.creationTime) ociAnnotations := generateOCIAnnotations(meta, operation.creationTime)
manifest := ocispec.Manifest{
Versioned: specs.Versioned{SchemaVersion: 2},
Config: configDescriptor,
Layers: layers,
Annotations: ociAnnotations,
}
manifestData, manifest, err := content.GenerateManifest(&configDescriptor, ociAnnotations, descriptors...) manifestData, err := json.Marshal(manifest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := memoryStore.StoreManifest(parsedRef.String(), manifest, manifestData); err != nil { manifestDescriptor, err := oras.TagBytes(ctx, memoryStore, ocispec.MediaTypeImageManifest, manifestData, ref)
if err != nil {
return nil, err return nil, err
} }
remotesResolver, err := c.resolver(parsedRef) repository, err := remote.NewRepository(parsedRef.String())
if err != nil { if err != nil {
return nil, err return nil, err
} }
registryStore := content.Registry{Resolver: remotesResolver} repository.PlainHTTP = c.plainHTTP
_, err = oras.Copy(ctx(c.out, c.debug), memoryStore, parsedRef.String(), registryStore, "", repository.Client = c.authorizer
oras.WithNameValidation(nil))
manifestDescriptor, err = oras.ExtendedCopy(ctx, memoryStore, parsedRef.String(), repository, parsedRef.String(), oras.DefaultExtendedCopyOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }
chartSummary := &descriptorPushSummaryWithMeta{ chartSummary := &descriptorPushSummaryWithMeta{
Meta: meta, Meta: meta,
} }
@ -611,8 +679,8 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu
chartSummary.Size = chartDescriptor.Size chartSummary.Size = chartDescriptor.Size
result := &PushResult{ result := &PushResult{
Manifest: &descriptorPushSummary{ Manifest: &descriptorPushSummary{
Digest: manifest.Digest.String(), Digest: manifestDescriptor.Digest.String(),
Size: manifest.Size, Size: manifestDescriptor.Size,
}, },
Config: &descriptorPushSummary{ Config: &descriptorPushSummary{
Digest: configDescriptor.Digest.String(), Digest: configDescriptor.Digest.String(),
@ -666,27 +734,29 @@ func (c *Client) Tags(ref string) ([]string, error) {
return nil, err return nil, err
} }
repository := registryremote.Repository{ ctx := context.Background()
Reference: parsedReference, repository, err := remote.NewRepository(parsedReference.String())
Client: c.registryAuthorizer,
PlainHTTP: c.plainHTTP,
}
var registryTags []string
registryTags, err = registry.Tags(ctx(c.out, c.debug), &repository)
if err != nil { if err != nil {
return nil, err return nil, err
} }
repository.PlainHTTP = c.plainHTTP
repository.Client = c.authorizer
var tagVersions []*semver.Version var tagVersions []*semver.Version
for _, tag := range registryTags { err = repository.Tags(ctx, "", func(tags []string) error {
// Change underscore (_) back to plus (+) for Helm for _, tag := range tags {
// See https://github.com/helm/helm/issues/10166 // Change underscore (_) back to plus (+) for Helm
tagVersion, err := semver.StrictNewVersion(strings.ReplaceAll(tag, "_", "+")) // See https://github.com/helm/helm/issues/10166
if err == nil { tagVersion, err := semver.StrictNewVersion(strings.ReplaceAll(tag, "_", "+"))
tagVersions = append(tagVersions, tagVersion) if err == nil {
tagVersions = append(tagVersions, tagVersion)
}
} }
return nil
})
if err != nil {
return nil, err
} }
// Sort the collection // Sort the collection

@ -17,12 +17,13 @@ limitations under the License.
package registry package registry
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"testing" "testing"
"github.com/containerd/containerd/errdefs"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"oras.land/oras-go/v2/content"
) )
type HTTPRegistryClientTestSuite struct { type HTTPRegistryClientTestSuite struct {
@ -42,6 +43,18 @@ func (suite *HTTPRegistryClientTestSuite) TearDownSuite() {
os.RemoveAll(suite.WorkspaceDir) os.RemoveAll(suite.WorkspaceDir)
} }
func (suite *HTTPRegistryClientTestSuite) Test_0_Login() {
err := suite.RegistryClient.Login(suite.DockerRegistryHost,
LoginOptBasicAuth("badverybad", "ohsobad"),
LoginOptPlainText(true))
suite.NotNil(err, "error logging into registry with bad credentials")
err = suite.RegistryClient.Login(suite.DockerRegistryHost,
LoginOptBasicAuth(testUsername, testPassword),
LoginOptPlainText(true))
suite.Nil(err, "no error logging into registry with good credentials")
}
func (suite *HTTPRegistryClientTestSuite) Test_1_Push() { func (suite *HTTPRegistryClientTestSuite) Test_1_Push() {
testPush(&suite.TestSuite) testPush(&suite.TestSuite)
} }
@ -60,7 +73,7 @@ func (suite *HTTPRegistryClientTestSuite) Test_4_ManInTheMiddle() {
// returns content that does not match the expected digest // returns content that does not match the expected digest
_, err := suite.RegistryClient.Pull(ref) _, err := suite.RegistryClient.Pull(ref)
suite.NotNil(err) suite.NotNil(err)
suite.True(errdefs.IsFailedPrecondition(err)) suite.True(errors.Is(err, content.ErrMismatchedDigest))
} }
func TestHTTPRegistryClientTestSuite(t *testing.T) { func TestHTTPRegistryClientTestSuite(t *testing.T) {

@ -66,7 +66,8 @@ func (suite *InsecureTLSRegistryClientTestSuite) Test_3_Tags() {
func (suite *InsecureTLSRegistryClientTestSuite) Test_4_Logout() { func (suite *InsecureTLSRegistryClientTestSuite) Test_4_Logout() {
err := suite.RegistryClient.Logout("this-host-aint-real:5000") err := suite.RegistryClient.Logout("this-host-aint-real:5000")
suite.NotNil(err, "error logging out of registry that has no entry") // in oras-credentials-go removing a nonexisting credential causes no errors
suite.Nil(err, "no error logging out of registry that has no entry")
err = suite.RegistryClient.Logout(suite.DockerRegistryHost) err = suite.RegistryClient.Logout(suite.DockerRegistryHost)
suite.Nil(err, "no error logging out of registry") suite.Nil(err, "no error logging out of registry")

@ -66,7 +66,8 @@ func (suite *TLSRegistryClientTestSuite) Test_3_Tags() {
func (suite *TLSRegistryClientTestSuite) Test_4_Logout() { func (suite *TLSRegistryClientTestSuite) Test_4_Logout() {
err := suite.RegistryClient.Logout("this-host-aint-real:5000") err := suite.RegistryClient.Logout("this-host-aint-real:5000")
suite.NotNil(err, "error logging out of registry that has no entry") // in oras-credentials-go removing a nonexisting credential causes no errors
suite.Nil(err, "no error logging out of registry that has no entry")
err = suite.RegistryClient.Logout(suite.DockerRegistryHost) err = suite.RegistryClient.Logout(suite.DockerRegistryHost)
suite.Nil(err, "no error logging out of registry") suite.Nil(err, "no error logging out of registry")

@ -18,7 +18,6 @@ package registry // import "helm.sh/helm/v3/pkg/registry"
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -30,9 +29,7 @@ import (
"github.com/Masterminds/semver/v3" "github.com/Masterminds/semver/v3"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "oras.land/oras-go/v2/registry"
orascontext "oras.land/oras-go/pkg/context"
"oras.land/oras-go/pkg/registry"
"helm.sh/helm/v3/internal/tlsutil" "helm.sh/helm/v3/internal/tlsutil"
"helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart"
@ -104,17 +101,6 @@ func extractChartMeta(chartData []byte) (*chart.Metadata, error) {
return ch.Metadata, nil return ch.Metadata, nil
} }
// ctx retrieves a fresh context.
// disable verbose logging coming from ORAS (unless debug is enabled)
func ctx(out io.Writer, debug bool) context.Context {
if !debug {
return orascontext.Background()
}
ctx := orascontext.WithLoggerFromWriter(context.Background(), out)
orascontext.GetLogger(ctx).Logger.SetLevel(logrus.DebugLevel)
return ctx
}
// parseReference will parse and validate the reference, and clean tags when // parseReference will parse and validate the reference, and clean tags when
// applicable tags are only cleaned when plus (+) signs are present, and are // applicable tags are only cleaned when plus (+) signs are present, and are
// converted to underscores (_) before pushing // converted to underscores (_) before pushing

@ -88,7 +88,6 @@ func setup(suite *TestSuite, tlsEnabled, insecure bool) *registry.Registry {
ClientOptEnableCache(true), ClientOptEnableCache(true),
ClientOptWriter(suite.Out), ClientOptWriter(suite.Out),
ClientOptCredentialsFile(credentialsFile), ClientOptCredentialsFile(credentialsFile),
ClientOptResolver(nil),
} }
if tlsEnabled { if tlsEnabled {
@ -139,14 +138,11 @@ func setup(suite *TestSuite, tlsEnabled, insecure bool) *registry.Registry {
config.HTTP.DrainTimeout = time.Duration(10) * time.Second config.HTTP.DrainTimeout = time.Duration(10) * time.Second
config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}} config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}}
// Basic auth is not possible if we are serving HTTP. config.Auth = configuration.Auth{
if tlsEnabled { "htpasswd": configuration.Parameters{
config.Auth = configuration.Auth{ "realm": "localhost",
"htpasswd": configuration.Parameters{ "path": htpasswdPath,
"realm": "localhost", },
"path": htpasswdPath,
},
}
} }
// config tls // config tls
@ -275,7 +271,7 @@ func testPush(suite *TestSuite) {
result, err := suite.RegistryClient.Push(chartData, ref, PushOptProvData(provData), PushOptCreationTime(testingChartCreationTime)) result, err := suite.RegistryClient.Push(chartData, ref, PushOptProvData(provData), PushOptCreationTime(testingChartCreationTime))
suite.Nil(err, "no error pushing good ref with prov") suite.Nil(err, "no error pushing good ref with prov")
_, err = suite.RegistryClient.Pull(ref) _, err = suite.RegistryClient.Pull(ref, PullOptWithProv(true))
suite.Nil(err, "no error pulling a simple chart") suite.Nil(err, "no error pulling a simple chart")
// Validate the output // Validate the output

@ -163,7 +163,8 @@ func (srv *OCIServer) Run(t *testing.T, opts ...OCIServerOpt) {
err = registryClient.Login( err = registryClient.Login(
srv.RegistryURL, srv.RegistryURL,
ociRegistry.LoginOptBasicAuth(srv.TestUsername, srv.TestPassword), ociRegistry.LoginOptBasicAuth(srv.TestUsername, srv.TestPassword),
ociRegistry.LoginOptInsecure(false)) ociRegistry.LoginOptInsecure(false),
ociRegistry.LoginOptPlainText(true))
if err != nil { if err != nil {
t.Fatalf("error logging into registry with good credentials") t.Fatalf("error logging into registry with good credentials")
} }

Loading…
Cancel
Save