1
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
33 type OCIGetter struct {
34 opts options
35 transport *http.Transport
36 once sync.Once
37 }
38
39
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
50
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
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
109 DisableCompression: true,
110 DialContext: (&net.Dialer{
111
112
113
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