1
2 package render
3
4 import (
5 "bytes"
6 "fmt"
7 "net/http"
8 "path"
9 "path/filepath"
10 "strings"
11
12 "github.com/linkerd/linkerd2/pkg/charts"
13 "helm.sh/helm/v3/pkg/chart/loader"
14 "helm.sh/helm/v3/pkg/chartutil"
15 "helm.sh/helm/v3/pkg/engine"
16 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
17
18 linkerd "edge-infra.dev/pkg/edge/linkerd"
19 l5dvalues "edge-infra.dev/pkg/edge/linkerd/helm/values"
20 "edge-infra.dev/pkg/edge/linkerd/policy"
21 "edge-infra.dev/pkg/k8s/decoder"
22 l5dchart "edge-infra.dev/third_party/k8s/linkerd/helm"
23 )
24
25
26
27 type Option func(*l5dvalues.Values)
28
29
30 func WithPriorityClass(priorityClass string) Option {
31 return func(v *l5dvalues.Values) {
32 v.PriorityClassName = priorityClass
33 }
34 }
35
36
37 func WithProxyIptablesMode(mode string) Option {
38 return func(v *l5dvalues.Values) {
39 v.ProxyInit.IptablesMode = mode
40 }
41 }
42
43
44
45
46 func WithExternalIDIssuer(crt string) Option {
47 return func(v *l5dvalues.Values) {
48 v.IdentityTrustAnchorsPEM = crt
49 v.Identity.Issuer.Scheme = "kubernetes.io/tls"
50 }
51 }
52
53
54 func WithHighAvailability() Option {
55 return func(v *l5dvalues.Values) {
56 v.HighAvailability = true
57 }
58 }
59
60
61
62 func WithAuthPolicy(policy string) Option {
63 return func(v *l5dvalues.Values) {
64 v.Proxy.DefaultInboundPolicy = policy
65 }
66 }
67
68
69 func WithClusterNetworks(clusterNetworks string) Option {
70 return func(v *l5dvalues.Values) {
71 v.ClusterNetworks = clusterNetworks
72 }
73 }
74
75
76
77
78 func WithContainerRegistry(registry string, pullSecretName string) Option {
79 return func(v *l5dvalues.Values) {
80 v.PolicyController.Image.Name = path.Join(registry, l5dchart.PolicyControllerImage)
81 v.ProxyInit.Image.Name = path.Join(registry, l5dchart.ProxyInitImage)
82 v.DebugContainer.Image.Name = path.Join(registry, l5dchart.DebugImage)
83 v.ControllerImage = path.Join(registry, l5dchart.ControllerImage)
84
85
86
87 v.Proxy.Image.Name = path.Join(registry, "proxy-ha")
88
89 if pullSecretName != "" {
90 v.ImagePullSecrets = []map[string]string{
91 {"name": pullSecretName},
92 }
93 }
94 }
95 }
96
97
98
99
100
101
102
103
104
105
106
107 func WithLogLevel(level string, expr ...string) Option {
108 return func(v *l5dvalues.Values) {
109
110 v.ControllerLogLevel = level
111
112 e := level
113 if len(expr) > 0 {
114 e = e + "," + strings.Join(expr, ",")
115 }
116 v.Proxy.LogLevel = e
117 v.PolicyController.LogLevel = e
118 }
119 }
120
121
122
123 func WithProxyLogLevel(expr string) Option {
124 return func(v *l5dvalues.Values) {
125 v.Proxy.LogLevel = expr
126 }
127 }
128
129
130
131 func WithPolicyControllerLogLevel(expr string) Option {
132 return func(v *l5dvalues.Values) {
133 v.PolicyController.LogLevel = expr
134 }
135 }
136
137
138 func WithLogFormat(format string) Option {
139 return func(v *l5dvalues.Values) {
140 v.ControllerLogFormat = format
141 v.Proxy.LogFormat = format
142 v.ProxyInit.LogFormat = format
143 }
144 }
145
146
147 func WithProxyInitRunAsRoot() Option {
148 return func(v *l5dvalues.Values) {
149 v.ProxyInit.RunAsRoot = true
150 }
151 }
152
153
154 func WithProxyInitResourceRequest() Option {
155 return func(v *l5dvalues.Values) {
156
157
158
159 v.ProxyInit.Resources.CPU.Request = "10m"
160 }
161 }
162
163
164 func WithThickPosConfiguration(issuanceLifetime string) Option {
165 return func(v *l5dvalues.Values) {
166 v.Identity.Issuer.IssuanceLifetime = issuanceLifetime
167 }
168 }
169
170
171
172 func WithNativeSidecar(enabled bool) Option {
173 return func(v *l5dvalues.Values) {
174 v.Proxy.NativeSidecar = enabled
175 }
176 }
177
178
179
180 func Manifests(opts ...Option) (map[string]string, error) {
181 values, err := l5dchart.DefaultValues()
182 if err != nil {
183 return nil, fmt.Errorf("failed to load charts default values: %w", err)
184 }
185
186
187
188
189 opts = append([]Option{
190
191
192
193 WithLogFormat(linkerd.JSONLogFormat),
194 WithProxyLogLevel("warn,error"),
195
196 WithAuthPolicy(policy.Default),
197 WithProxyInitRunAsRoot(),
198 WithProxyInitResourceRequest(),
199 WithProxyIptablesMode(linkerd.IptablesModes),
200 }, opts...)
201
202
203
204 for _, o := range opts {
205 o(values)
206 }
207
208 files, err := readL5dFiles()
209 if err != nil {
210 return nil, fmt.Errorf("Failed reading in linkerd manifests: %w", err)
211 }
212
213 partials, err := readPartialFiles()
214 if err != nil {
215 return nil, fmt.Errorf("Failed reading in linkerd partials: %w", err)
216 }
217
218 chart, err := loader.LoadFiles(append(files, partials...))
219 if err != nil {
220 return nil, fmt.Errorf("Failed loading manifests: %w", err)
221 }
222
223 vmap, err := values.ToMap()
224 if err != nil {
225 return nil, fmt.Errorf("failed to convert values into map: %w", err)
226 }
227
228 rendered, err := engine.Render(chart, map[string]interface{}{"Values": vmap, "Release": map[string]interface{}{"Namespace": "linkerd"}})
229 if err != nil {
230 return nil, fmt.Errorf("failed to render manifests: %w", err)
231 }
232 return rendered, nil
233 }
234
235
236
237 func Unstructured(opts ...Option) ([]*unstructured.Unstructured, error) {
238 manifests, err := Manifests(opts...)
239 if err != nil {
240 return nil, fmt.Errorf("failed to render manifests: %w", err)
241 }
242
243 var buf bytes.Buffer
244
245 for filepath, contents := range manifests {
246
247 if !strings.HasSuffix(filepath, ".yaml") {
248 continue
249 }
250 if _, err := buf.WriteString(contents); err != nil {
251 return nil, fmt.Errorf("failed to write rendered manifest to buffer: %w", err)
252 }
253 }
254
255 resources, err := decoder.DecodeYAML(buf.Bytes())
256 if err != nil {
257 return nil, fmt.Errorf("failed to decode yaml data into struct: %w", err)
258 }
259
260 return resources, nil
261 }
262
263 func readL5dFiles() ([]*loader.BufferedFile, error) {
264 controlFiles := []*loader.BufferedFile{
265 {Name: chartutil.ChartfileName},
266 {Name: chartutil.ValuesfileName},
267 }
268
269 control := filepath.Join(l5dchart.ControlPath, chartutil.TemplatesDir)
270 controlTemplates, err := l5dchart.Templates.ReadDir(control)
271 if err != nil {
272 return nil, fmt.Errorf("failed to read %s: %w", l5dchart.ControlPath, err)
273 }
274
275 for _, p := range controlTemplates {
276 controlFiles = append(controlFiles, &loader.BufferedFile{
277 Name: filepath.Join(chartutil.TemplatesDir, p.Name()),
278 })
279 }
280
281 if err := charts.FilesReader(http.FS(l5dchart.Templates), l5dchart.ControlPath+"/", controlFiles); err != nil {
282 return nil, fmt.Errorf("failed to read linkerd chart into buffered files: %w", err)
283 }
284
285 return controlFiles, nil
286 }
287
288 func readPartialFiles() ([]*loader.BufferedFile, error) {
289 files := []*loader.BufferedFile{
290 {Name: chartutil.ChartfileName},
291 {Name: chartutil.ValuesfileName},
292 }
293
294 partials := filepath.Join(l5dchart.PartialsChartPath, chartutil.TemplatesDir)
295 L5dPartials, err := l5dchart.Templates.ReadDir(partials)
296 if err != nil {
297 return nil, fmt.Errorf("failed to read %s: %w", l5dchart.PartialsChartPath, err)
298 }
299
300 for _, t := range L5dPartials {
301 files = append(files, &loader.BufferedFile{
302 Name: filepath.Join(chartutil.TemplatesDir, t.Name()),
303 })
304 }
305
306
307
308 if err := charts.FilesReader(http.FS(l5dchart.Templates), l5dchart.PartialsChartPath+"/", files); err != nil {
309 return nil, fmt.Errorf("failed to read partials chart into buffered files: %w", err)
310 }
311
312 return files, nil
313 }
314
View as plain text