...

Source file src/helm.sh/helm/v3/pkg/getter/ocigetter.go

Documentation: helm.sh/helm/v3/pkg/getter

     1  /*
     2  Copyright The Helm Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package getter
    17  
    18  import (
    19  	"bytes"
    20  	"fmt"
    21  	"net"
    22  	"net/http"
    23  	"strings"
    24  	"sync"
    25  	"time"
    26  
    27  	"helm.sh/helm/v3/internal/tlsutil"
    28  	"helm.sh/helm/v3/internal/urlutil"
    29  	"helm.sh/helm/v3/pkg/registry"
    30  )
    31  
    32  // OCIGetter is the default HTTP(/S) backend handler
    33  type OCIGetter struct {
    34  	opts      options
    35  	transport *http.Transport
    36  	once      sync.Once
    37  }
    38  
    39  // Get performs a Get from repo.Getter and returns the body.
    40  func (g *OCIGetter) Get(href string, options ...Option) (*bytes.Buffer, error) {
    41  	for _, opt := range options {
    42  		opt(&g.opts)
    43  	}
    44  	return g.get(href)
    45  }
    46  
    47  func (g *OCIGetter) get(href string) (*bytes.Buffer, error) {
    48  	client := g.opts.registryClient
    49  	// if the user has already provided a configured registry client, use it,
    50  	// this is particularly true when user has his own way of handling the client credentials.
    51  	if client == nil {
    52  		c, err := g.newRegistryClient()
    53  		if err != nil {
    54  			return nil, err
    55  		}
    56  		client = c
    57  	}
    58  
    59  	ref := strings.TrimPrefix(href, fmt.Sprintf("%s://", registry.OCIScheme))
    60  
    61  	var pullOpts []registry.PullOption
    62  	requestingProv := strings.HasSuffix(ref, ".prov")
    63  	if requestingProv {
    64  		ref = strings.TrimSuffix(ref, ".prov")
    65  		pullOpts = append(pullOpts,
    66  			registry.PullOptWithChart(false),
    67  			registry.PullOptWithProv(true))
    68  	}
    69  
    70  	result, err := client.Pull(ref, pullOpts...)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	if requestingProv {
    76  		return bytes.NewBuffer(result.Prov.Data), nil
    77  	}
    78  	return bytes.NewBuffer(result.Chart.Data), nil
    79  }
    80  
    81  // NewOCIGetter constructs a valid http/https client as a Getter
    82  func NewOCIGetter(ops ...Option) (Getter, error) {
    83  	var client OCIGetter
    84  
    85  	for _, opt := range ops {
    86  		opt(&client.opts)
    87  	}
    88  
    89  	return &client, nil
    90  }
    91  
    92  func (g *OCIGetter) newRegistryClient() (*registry.Client, error) {
    93  	if g.opts.transport != nil {
    94  		client, err := registry.NewClient(
    95  			registry.ClientOptHTTPClient(&http.Client{
    96  				Transport: g.opts.transport,
    97  				Timeout:   g.opts.timeout,
    98  			}),
    99  		)
   100  		if err != nil {
   101  			return nil, err
   102  		}
   103  		return client, nil
   104  	}
   105  
   106  	g.once.Do(func() {
   107  		g.transport = &http.Transport{
   108  			// From https://github.com/google/go-containerregistry/blob/31786c6cbb82d6ec4fb8eb79cd9387905130534e/pkg/v1/remote/options.go#L87
   109  			DisableCompression: true,
   110  			DialContext: (&net.Dialer{
   111  				// By default we wrap the transport in retries, so reduce the
   112  				// default dial timeout to 5s to avoid 5x 30s of connection
   113  				// timeouts when doing the "ping" on certain http registries.
   114  				Timeout:   5 * time.Second,
   115  				KeepAlive: 30 * time.Second,
   116  			}).DialContext,
   117  			ForceAttemptHTTP2:     true,
   118  			MaxIdleConns:          100,
   119  			IdleConnTimeout:       90 * time.Second,
   120  			TLSHandshakeTimeout:   10 * time.Second,
   121  			ExpectContinueTimeout: 1 * time.Second,
   122  			Proxy:                 http.ProxyFromEnvironment,
   123  		}
   124  	})
   125  
   126  	if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" || g.opts.insecureSkipVerifyTLS {
   127  		tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile, g.opts.insecureSkipVerifyTLS)
   128  		if err != nil {
   129  			return nil, fmt.Errorf("can't create TLS config for client: %w", err)
   130  		}
   131  
   132  		sni, err := urlutil.ExtractHostname(g.opts.url)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  		tlsConf.ServerName = sni
   137  
   138  		g.transport.TLSClientConfig = tlsConf
   139  	}
   140  
   141  	opts := []registry.ClientOption{registry.ClientOptHTTPClient(&http.Client{
   142  		Transport: g.transport,
   143  		Timeout:   g.opts.timeout,
   144  	})}
   145  	if g.opts.plainHTTP {
   146  		opts = append(opts, registry.ClientOptPlainHTTP())
   147  	}
   148  
   149  	client, err := registry.NewClient(opts...)
   150  
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	return client, nil
   156  }
   157  

View as plain text