1/*
2Copyright The Helm Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package getter
18
19import (
20	"bytes"
21	"time"
22
23	"github.com/pkg/errors"
24
25	"helm.sh/helm/v3/internal/experimental/registry"
26	"helm.sh/helm/v3/pkg/cli"
27)
28
29// options are generic parameters to be provided to the getter during instantiation.
30//
31// Getters may or may not ignore these parameters as they are passed in.
32type options struct {
33	url                   string
34	certFile              string
35	keyFile               string
36	caFile                string
37	unTar                 bool
38	insecureSkipVerifyTLS bool
39	username              string
40	password              string
41	userAgent             string
42	version               string
43	registryClient        *registry.Client
44	timeout               time.Duration
45}
46
47// Option allows specifying various settings configurable by the user for overriding the defaults
48// used when performing Get operations with the Getter.
49type Option func(*options)
50
51// WithURL informs the getter the server name that will be used when fetching objects. Used in conjunction with
52// WithTLSClientConfig to set the TLSClientConfig's server name.
53func WithURL(url string) Option {
54	return func(opts *options) {
55		opts.url = url
56	}
57}
58
59// WithBasicAuth sets the request's Authorization header to use the provided credentials
60func WithBasicAuth(username, password string) Option {
61	return func(opts *options) {
62		opts.username = username
63		opts.password = password
64	}
65}
66
67// WithUserAgent sets the request's User-Agent header to use the provided agent name.
68func WithUserAgent(userAgent string) Option {
69	return func(opts *options) {
70		opts.userAgent = userAgent
71	}
72}
73
74// WithInsecureSkipVerifyTLS determines if a TLS Certificate will be checked
75func WithInsecureSkipVerifyTLS(insecureSkipVerifyTLS bool) Option {
76	return func(opts *options) {
77		opts.insecureSkipVerifyTLS = insecureSkipVerifyTLS
78	}
79}
80
81// WithTLSClientConfig sets the client auth with the provided credentials.
82func WithTLSClientConfig(certFile, keyFile, caFile string) Option {
83	return func(opts *options) {
84		opts.certFile = certFile
85		opts.keyFile = keyFile
86		opts.caFile = caFile
87	}
88}
89
90// WithTimeout sets the timeout for requests
91func WithTimeout(timeout time.Duration) Option {
92	return func(opts *options) {
93		opts.timeout = timeout
94	}
95}
96
97func WithTagName(tagname string) Option {
98	return func(opts *options) {
99		opts.version = tagname
100	}
101}
102
103func WithRegistryClient(client *registry.Client) Option {
104	return func(opts *options) {
105		opts.registryClient = client
106	}
107}
108
109func WithUntar() Option {
110	return func(opts *options) {
111		opts.unTar = true
112	}
113}
114
115// Getter is an interface to support GET to the specified URL.
116type Getter interface {
117	// Get file content by url string
118	Get(url string, options ...Option) (*bytes.Buffer, error)
119}
120
121// Constructor is the function for every getter which creates a specific instance
122// according to the configuration
123type Constructor func(options ...Option) (Getter, error)
124
125// Provider represents any getter and the schemes that it supports.
126//
127// For example, an HTTP provider may provide one getter that handles both
128// 'http' and 'https' schemes.
129type Provider struct {
130	Schemes []string
131	New     Constructor
132}
133
134// Provides returns true if the given scheme is supported by this Provider.
135func (p Provider) Provides(scheme string) bool {
136	for _, i := range p.Schemes {
137		if i == scheme {
138			return true
139		}
140	}
141	return false
142}
143
144// Providers is a collection of Provider objects.
145type Providers []Provider
146
147// ByScheme returns a Provider that handles the given scheme.
148//
149// If no provider handles this scheme, this will return an error.
150func (p Providers) ByScheme(scheme string) (Getter, error) {
151	for _, pp := range p {
152		if pp.Provides(scheme) {
153			return pp.New()
154		}
155	}
156	return nil, errors.Errorf("scheme %q not supported", scheme)
157}
158
159var httpProvider = Provider{
160	Schemes: []string{"http", "https"},
161	New:     NewHTTPGetter,
162}
163
164var ociProvider = Provider{
165	Schemes: []string{"oci"},
166	New:     NewOCIGetter,
167}
168
169// All finds all of the registered getters as a list of Provider instances.
170// Currently, the built-in getters and the discovered plugins with downloader
171// notations are collected.
172func All(settings *cli.EnvSettings) Providers {
173	result := Providers{httpProvider, ociProvider}
174	pluginDownloaders, _ := collectPlugins(settings)
175	result = append(result, pluginDownloaders...)
176	return result
177}
178