Skip to content

Commit

Permalink
oci: Add flag --plain-http to enable working with HTTP registries
Browse files Browse the repository at this point in the history
Add a new flag `--plain-http` to the following commands:
* `helm install`
* `helm pull`
* `helm push`
* `helm template`
* `helm upgrade`
* `helm show`

This flag instructs the registry client to use plain HTTP connections,
thus enabling upload/download of charts from OCI registries served at
an HTTP endpoint.

Signed-off-by: Sanskar Jaiswal <jaiswalsanskar078@gmail.com>
  • Loading branch information
aryan9600 committed Jun 9, 2023
1 parent 1c4885f commit 2538b92
Show file tree
Hide file tree
Showing 16 changed files with 82 additions and 35 deletions.
1 change: 1 addition & 0 deletions cmd/helm/flags.go
Expand Up @@ -61,6 +61,7 @@ func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) {
f.StringVar(&c.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&c.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.BoolVar(&c.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download")
f.BoolVar(&c.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download")
f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&c.PassCredentialsAll, "pass-credentials", false, "pass credentials to all domains")
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/helm/install.go
Expand Up @@ -136,7 +136,8 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return compInstall(args, toComplete, client)
},
RunE: func(_ *cobra.Command, args []string) error {
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, client.InsecureSkipTLSverify)
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/helm/pull.go
Expand Up @@ -64,7 +64,8 @@ func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client.Version = ">0.0.0-0"
}

registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, client.InsecureSkipTLSverify)
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
Expand Down
5 changes: 4 additions & 1 deletion cmd/helm/push.go
Expand Up @@ -39,6 +39,7 @@ type registryPushOptions struct {
keyFile string
caFile string
insecureSkipTLSverify bool
plainHTTP bool
}

func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Expand Down Expand Up @@ -67,7 +68,7 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return nil, cobra.ShellCompDirectiveNoFileComp
},
RunE: func(cmd *cobra.Command, args []string) error {
registryClient, err := newRegistryClient(o.certFile, o.keyFile, o.caFile, o.insecureSkipTLSverify)
registryClient, err := newRegistryClient(o.certFile, o.keyFile, o.caFile, o.insecureSkipTLSverify, o.plainHTTP)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
Expand All @@ -77,6 +78,7 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client := action.NewPushWithOpts(action.WithPushConfig(cfg),
action.WithTLSClientConfig(o.certFile, o.keyFile, o.caFile),
action.WithInsecureSkipTLSVerify(o.insecureSkipTLSverify),
action.WithPlainHTTP(o.plainHTTP),
action.WithPushOptWriter(out))
client.Settings = settings
output, err := client.Run(chartRef, remote)
Expand All @@ -93,6 +95,7 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
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.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")

return cmd
}
19 changes: 12 additions & 7 deletions cmd/helm/root.go
Expand Up @@ -152,7 +152,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
flags.ParseErrorsWhitelist.UnknownFlags = true
flags.Parse(args)

registryClient, err := newDefaultRegistryClient()
registryClient, err := newDefaultRegistryClient(false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -257,29 +257,34 @@ func checkForExpiredRepos(repofile string) {

}

func newRegistryClient(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*registry.Client, error) {
func newRegistryClient(certFile, keyFile, caFile string, insecureSkipTLSverify, plainHTTP bool) (*registry.Client, error) {
if certFile != "" && keyFile != "" || caFile != "" || insecureSkipTLSverify {
registryClient, err := newRegistryClientWithTLS(certFile, keyFile, caFile, insecureSkipTLSverify)
if err != nil {
return nil, err
}
return registryClient, nil
}
registryClient, err := newDefaultRegistryClient()
registryClient, err := newDefaultRegistryClient(plainHTTP)
if err != nil {
return nil, err
}
return registryClient, nil
}

func newDefaultRegistryClient() (*registry.Client, error) {
// Create a new registry client
registryClient, err := registry.NewClient(
func newDefaultRegistryClient(plainHTTP bool) (*registry.Client, error) {
opts := []registry.ClientOption{
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(os.Stderr),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
)
}
if plainHTTP {
opts = append(opts, registry.ClientOptPlainHTTP())
}

// Create a new registry client
registryClient, err := registry.NewClient(opts...)
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/helm/show.go
Expand Up @@ -226,7 +226,8 @@ 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)
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/helm/template.go
Expand Up @@ -73,7 +73,8 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client.KubeVersion = parsedKubeVersion
}

registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, client.InsecureSkipTLSverify)
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/helm/upgrade.go
Expand Up @@ -90,7 +90,8 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
client.Namespace = settings.Namespace()

registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, client.InsecureSkipTLSverify)
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/action/install.go
Expand Up @@ -114,6 +114,7 @@ type ChartPathOptions struct {
CertFile string // --cert-file
KeyFile string // --key-file
InsecureSkipTLSverify bool // --insecure-skip-verify
PlainHTTP bool // --plain-http
Keyring string // --keyring
Password string // --password
PassCredentialsAll bool // --pass-credentials
Expand Down Expand Up @@ -733,6 +734,7 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
getter.WithPassCredentialsAll(c.PassCredentialsAll),
getter.WithTLSClientConfig(c.CertFile, c.KeyFile, c.CaFile),
getter.WithInsecureSkipVerifyTLS(c.InsecureSkipTLSverify),
getter.WithPlainHTTP(c.PlainHTTP),
},
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Expand Down
1 change: 1 addition & 0 deletions pkg/action/pull.go
Expand Up @@ -90,6 +90,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
getter.WithPassCredentialsAll(p.PassCredentialsAll),
getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile),
getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify),
getter.WithPlainHTTP(p.PlainHTTP),
},
RegistryClient: p.cfg.RegistryClient,
RepositoryConfig: p.Settings.RepositoryConfig,
Expand Down
9 changes: 9 additions & 0 deletions pkg/action/push.go
Expand Up @@ -36,6 +36,7 @@ type Push struct {
keyFile string
caFile string
insecureSkipTLSverify bool
plainHTTP bool
out io.Writer
}

Expand Down Expand Up @@ -65,6 +66,13 @@ func WithInsecureSkipTLSVerify(insecureSkipTLSVerify bool) PushOpt {
}
}

// WithPlainHTTP configures the use of plain HTTP connections.
func WithPlainHTTP(plainHTTP bool) PushOpt {
return func(p *Push) {
p.plainHTTP = plainHTTP
}
}

// WithOptWriter sets the registryOut field on the push configuration object.
func WithPushOptWriter(out io.Writer) PushOpt {
return func(p *Push) {
Expand All @@ -91,6 +99,7 @@ func (p *Push) Run(chartRef string, remote string) (string, error) {
Options: []pusher.Option{
pusher.WithTLSClientConfig(p.certFile, p.keyFile, p.caFile),
pusher.WithInsecureSkipTLSVerify(p.insecureSkipTLSverify),
pusher.WithPlainHTTP(p.plainHTTP),
},
}

Expand Down
7 changes: 7 additions & 0 deletions pkg/getter/getter.go
Expand Up @@ -37,6 +37,7 @@ type options struct {
caFile string
unTar bool
insecureSkipVerifyTLS bool
plainHTTP bool
username string
password string
passCredentialsAll bool
Expand Down Expand Up @@ -96,6 +97,12 @@ func WithTLSClientConfig(certFile, keyFile, caFile string) Option {
}
}

func WithPlainHTTP(plainHTTP bool) Option {
return func(opts *options) {
opts.plainHTTP = plainHTTP
}
}

// WithTimeout sets the timeout for requests
func WithTimeout(timeout time.Duration) Option {
return func(opts *options) {
Expand Down
15 changes: 9 additions & 6 deletions pkg/getter/ocigetter.go
Expand Up @@ -137,12 +137,15 @@ func (g *OCIGetter) newRegistryClient() (*registry.Client, error) {
g.transport.TLSClientConfig = tlsConf
}

client, err := registry.NewClient(
registry.ClientOptHTTPClient(&http.Client{
Transport: g.transport,
Timeout: g.opts.timeout,
}),
)
opts := []registry.ClientOption{registry.ClientOptHTTPClient(&http.Client{
Transport: g.transport,
Timeout: g.opts.timeout,
})}
if g.opts.plainHTTP {
opts = append(opts, registry.ClientOptPlainHTTP())
}

client, err := registry.NewClient(opts...)

if err != nil {
return nil, err
Expand Down
9 changes: 6 additions & 3 deletions pkg/pusher/ocipusher.go
Expand Up @@ -139,9 +139,12 @@ func (pusher *OCIPusher) newRegistryClient() (*registry.Client, error) {
return registryClient, nil
}

registryClient, err := registry.NewClient(
registry.ClientOptEnableCache(true),
)
opts := []registry.ClientOption{registry.ClientOptEnableCache(true)}
if pusher.opts.plainHTTP {
opts = append(opts, registry.ClientOptPlainHTTP())
}

registryClient, err := registry.NewClient(opts...)
if err != nil {
return nil, err
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/pusher/pusher.go
Expand Up @@ -32,6 +32,7 @@ type options struct {
keyFile string
caFile string
insecureSkipTLSverify bool
plainHTTP bool
}

// Option allows specifying various settings configurable by the user for overriding the defaults
Expand Down Expand Up @@ -61,6 +62,12 @@ func WithInsecureSkipTLSVerify(insecureSkipTLSVerify bool) Option {
}
}

func WithPlainHTTP(plainHTTP bool) Option {
return func(opts *options) {
opts.plainHTTP = plainHTTP
}
}

// Pusher is an interface to support upload to the specified URL.
type Pusher interface {
// Push file content by url string
Expand Down
27 changes: 14 additions & 13 deletions pkg/registry/client.go
Expand Up @@ -61,6 +61,7 @@ type (
registryAuthorizer *registryauth.Client
resolver remotes.Resolver
httpClient *http.Client
plainHTTP bool
}

// ClientOption allows specifying various settings configurable by the user for overriding the defaults
Expand Down Expand Up @@ -93,6 +94,9 @@ func NewClient(options ...ClientOption) (*Client, error) {
if client.httpClient != nil {
opts = append(opts, auth.WithResolverClient(client.httpClient))
}
if client.plainHTTP {
opts = append(opts, auth.WithResolverPlainHTTP())
}
resolver, err := client.authorizer.ResolverWithOpts(opts...)
if err != nil {
return nil, err
Expand Down Expand Up @@ -177,6 +181,12 @@ func ClientOptHTTPClient(httpClient *http.Client) ClientOption {
}
}

func ClientOptPlainHTTP() ClientOption {
return func(c *Client) {
c.plainHTTP = true
}
}

type (
// LoginOption allows specifying various settings on login
LoginOption func(*loginOperation)
Expand Down Expand Up @@ -626,23 +636,14 @@ func (c *Client) Tags(ref string) ([]string, error) {
repository := registryremote.Repository{
Reference: parsedReference,
Client: c.registryAuthorizer,
PlainHTTP: c.plainHTTP,
}

var registryTags []string

for {
registryTags, err = registry.Tags(ctx(c.out, c.debug), &repository)
if err != nil {
// Fallback to http based request
if !repository.PlainHTTP && strings.Contains(err.Error(), "server gave HTTP response") {
repository.PlainHTTP = true
continue
}
return nil, err
}

break

registryTags, err = registry.Tags(ctx(c.out, c.debug), &repository)
if err != nil {
return nil, err
}

var tagVersions []*semver.Version
Expand Down

0 comments on commit 2538b92

Please sign in to comment.