1 package inject
2
3 import (
4 "bytes"
5 "crypto/sha256"
6 "encoding/hex"
7 "encoding/json"
8 "errors"
9 "fmt"
10 "html/template"
11 "net"
12 "reflect"
13 "regexp"
14 "sort"
15 "strconv"
16 "strings"
17 "time"
18
19 jsonfilter "github.com/clarketm/json"
20 "github.com/linkerd/linkerd2/pkg/charts"
21 l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
22 "github.com/linkerd/linkerd2/pkg/charts/static"
23 "github.com/linkerd/linkerd2/pkg/k8s"
24 "github.com/linkerd/linkerd2/pkg/util"
25 log "github.com/sirupsen/logrus"
26 "helm.sh/helm/v3/pkg/chart/loader"
27 "helm.sh/helm/v3/pkg/chartutil"
28 appsv1 "k8s.io/api/apps/v1"
29 batchv1 "k8s.io/api/batch/v1"
30 corev1 "k8s.io/api/core/v1"
31 k8sResource "k8s.io/apimachinery/pkg/api/resource"
32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33 "k8s.io/apimachinery/pkg/runtime"
34 "k8s.io/apimachinery/pkg/util/intstr"
35 "sigs.k8s.io/yaml"
36 )
37
38 var (
39 rTrail = regexp.MustCompile(`\},\s*\]`)
40
41
42
43 ProxyAnnotations = []string{
44 k8s.ProxyAdminPortAnnotation,
45 k8s.ProxyControlPortAnnotation,
46 k8s.ProxyEnableDebugAnnotation,
47 k8s.ProxyEnableExternalProfilesAnnotation,
48 k8s.ProxyImagePullPolicyAnnotation,
49 k8s.ProxyInboundPortAnnotation,
50 k8s.ProxyInitImageAnnotation,
51 k8s.ProxyInitImageVersionAnnotation,
52 k8s.ProxyOutboundPortAnnotation,
53 k8s.ProxyPodInboundPortsAnnotation,
54 k8s.ProxyCPULimitAnnotation,
55 k8s.ProxyCPURequestAnnotation,
56 k8s.ProxyImageAnnotation,
57 k8s.ProxyLogFormatAnnotation,
58 k8s.ProxyLogLevelAnnotation,
59 k8s.ProxyMemoryLimitAnnotation,
60 k8s.ProxyMemoryRequestAnnotation,
61 k8s.ProxyEphemeralStorageLimitAnnotation,
62 k8s.ProxyEphemeralStorageRequestAnnotation,
63 k8s.ProxyUIDAnnotation,
64 k8s.ProxyGIDAnnotation,
65 k8s.ProxyVersionOverrideAnnotation,
66 k8s.ProxyRequireIdentityOnInboundPortsAnnotation,
67 k8s.ProxyIgnoreInboundPortsAnnotation,
68 k8s.ProxyOpaquePortsAnnotation,
69 k8s.ProxyIgnoreOutboundPortsAnnotation,
70 k8s.ProxyOutboundConnectTimeout,
71 k8s.ProxyInboundConnectTimeout,
72 k8s.ProxyAwait,
73 k8s.ProxyDefaultInboundPolicyAnnotation,
74 k8s.ProxySkipSubnetsAnnotation,
75 k8s.ProxyAccessLogAnnotation,
76 k8s.ProxyShutdownGracePeriodAnnotation,
77 k8s.ProxyOutboundDiscoveryCacheUnusedTimeout,
78 k8s.ProxyInboundDiscoveryCacheUnusedTimeout,
79 k8s.ProxyDisableOutboundProtocolDetectTimeout,
80 k8s.ProxyDisableInboundProtocolDetectTimeout,
81 }
82
83
84 ProxyAlphaConfigAnnotations = []string{
85 k8s.ProxyWaitBeforeExitSecondsAnnotation,
86 k8s.ProxyEnableNativeSidecarAnnotation,
87 }
88 )
89
90
91
92 type Origin int
93
94 const (
95
96
97 OriginCLI Origin = iota
98
99
100
101 OriginWebhook
102
103
104
105 OriginUnknown
106 )
107
108
109
110 type OwnerRetrieverFunc func(*corev1.Pod) (string, string, error)
111
112
113 type ResourceConfig struct {
114
115
116
117 values *l5dcharts.Values
118
119 namespace string
120
121
122
123
124 nsAnnotations map[string]string
125 ownerRetriever OwnerRetrieverFunc
126 origin Origin
127
128 workload struct {
129 obj runtime.Object
130 metaType metav1.TypeMeta
131
132
133 Meta *metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
134 ownerRef *metav1.OwnerReference
135 }
136
137 pod struct {
138 meta *metav1.ObjectMeta
139
140
141
142
143 labels map[string]string
144 annotations map[string]string
145 spec *corev1.PodSpec
146 }
147 }
148
149 type podPatch struct {
150 l5dcharts.Values
151 PathPrefix string `json:"pathPrefix"`
152 AddRootMetadata bool `json:"addRootMetadata"`
153 AddRootAnnotations bool `json:"addRootAnnotations"`
154 Annotations map[string]string `json:"annotations"`
155 AddRootLabels bool `json:"addRootLabels"`
156 AddRootInitContainers bool `json:"addRootInitContainers"`
157 AddRootVolumes bool `json:"addRootVolumes"`
158 Labels map[string]string `json:"labels"`
159 DebugContainer *l5dcharts.DebugContainer `json:"debugContainer"`
160 }
161
162 type annotationPatch struct {
163 AddRootAnnotations bool
164 OpaquePorts string
165 }
166
167
168
169
170 func AppendNamespaceAnnotations(base map[string]string, nsAnn map[string]string, workloadAnn map[string]string) {
171 ann := append(ProxyAnnotations, ProxyAlphaConfigAnnotations...)
172 ann = append(ann, k8s.ProxyInjectAnnotation)
173
174 for _, key := range ann {
175 if _, found := nsAnn[key]; !found {
176 continue
177 }
178 if val, ok := GetConfigOverride(key, workloadAnn, nsAnn); ok {
179 base[key] = val
180 }
181 }
182 }
183
184
185
186 func GetOverriddenValues(values *l5dcharts.Values, overrides map[string]string, namedPorts map[string]int32) (*l5dcharts.Values, error) {
187
188 copyValues, err := values.DeepCopy()
189 if err != nil {
190 return nil, err
191 }
192
193 applyAnnotationOverrides(copyValues, overrides, namedPorts)
194 return copyValues, nil
195 }
196
197 func applyAnnotationOverrides(values *l5dcharts.Values, annotations map[string]string, namedPorts map[string]int32) {
198
199 if override, ok := annotations[k8s.ProxyInjectAnnotation]; ok {
200 if override == k8s.ProxyInjectIngress {
201 values.Proxy.IsIngress = true
202 }
203 }
204
205 if override, ok := annotations[k8s.ProxyImageAnnotation]; ok {
206 values.Proxy.Image.Name = override
207 }
208
209 if override, ok := annotations[k8s.ProxyVersionOverrideAnnotation]; ok {
210 values.Proxy.Image.Version = override
211 }
212
213 if override, ok := annotations[k8s.ProxyImagePullPolicyAnnotation]; ok {
214 values.Proxy.Image.PullPolicy = override
215 }
216
217 if override, ok := annotations[k8s.ProxyInitImageVersionAnnotation]; ok {
218 values.ProxyInit.Image.Version = override
219 }
220
221 if override, ok := annotations[k8s.ProxyControlPortAnnotation]; ok {
222 controlPort, err := strconv.ParseInt(override, 10, 32)
223 if err == nil {
224 values.Proxy.Ports.Control = int32(controlPort)
225 }
226 }
227
228 if override, ok := annotations[k8s.ProxyInboundPortAnnotation]; ok {
229 inboundPort, err := strconv.ParseInt(override, 10, 32)
230 if err == nil {
231 values.Proxy.Ports.Inbound = int32(inboundPort)
232 }
233 }
234
235 if override, ok := annotations[k8s.ProxyAdminPortAnnotation]; ok {
236 adminPort, err := strconv.ParseInt(override, 10, 32)
237 if err == nil {
238 values.Proxy.Ports.Admin = int32(adminPort)
239 }
240 }
241
242 if override, ok := annotations[k8s.ProxyOutboundPortAnnotation]; ok {
243 outboundPort, err := strconv.ParseInt(override, 10, 32)
244 if err == nil {
245 values.Proxy.Ports.Outbound = int32(outboundPort)
246 }
247 }
248
249 if override, ok := annotations[k8s.ProxyPodInboundPortsAnnotation]; ok {
250 values.Proxy.PodInboundPorts = override
251 }
252
253 if override, ok := annotations[k8s.ProxyLogLevelAnnotation]; ok {
254 values.Proxy.LogLevel = override
255 }
256
257 if override, ok := annotations[k8s.ProxyLogFormatAnnotation]; ok {
258 values.Proxy.LogFormat = override
259 }
260
261 if override, ok := annotations[k8s.ProxyRequireIdentityOnInboundPortsAnnotation]; ok {
262 values.Proxy.RequireIdentityOnInboundPorts = override
263 }
264
265 if override, ok := annotations[k8s.ProxyOutboundConnectTimeout]; ok {
266 duration, err := time.ParseDuration(override)
267 if err != nil {
268 log.Warnf("unrecognized proxy-outbound-connect-timeout duration value found on pod annotation: %s", err.Error())
269 } else {
270 values.Proxy.OutboundConnectTimeout = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
271 }
272 }
273
274 if override, ok := annotations[k8s.ProxyInboundConnectTimeout]; ok {
275 duration, err := time.ParseDuration(override)
276 if err != nil {
277 log.Warnf("unrecognized proxy-inbound-connect-timeout duration value found on pod annotation: %s", err.Error())
278 } else {
279 values.Proxy.InboundConnectTimeout = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
280 }
281 }
282
283 if override, ok := annotations[k8s.ProxyOutboundDiscoveryCacheUnusedTimeout]; ok {
284 duration, err := time.ParseDuration(override)
285 if err != nil {
286 log.Warnf("unrecognized duration value used on pod annotation %s: %s", k8s.ProxyOutboundDiscoveryCacheUnusedTimeout, err.Error())
287 } else {
288 values.Proxy.OutboundDiscoveryCacheUnusedTimeout = fmt.Sprintf("%ds", int(duration.Seconds()))
289 }
290 }
291
292 if override, ok := annotations[k8s.ProxyInboundDiscoveryCacheUnusedTimeout]; ok {
293 duration, err := time.ParseDuration(override)
294 if err != nil {
295 log.Warnf("unrecognized duration value used on pod annotation %s: %s", k8s.ProxyInboundDiscoveryCacheUnusedTimeout, err.Error())
296 } else {
297 values.Proxy.InboundDiscoveryCacheUnusedTimeout = fmt.Sprintf("%ds", int(duration.Seconds()))
298 }
299 }
300
301 if override, ok := annotations[k8s.ProxyDisableOutboundProtocolDetectTimeout]; ok {
302 value, err := strconv.ParseBool(override)
303 if err == nil {
304 values.Proxy.DisableOutboundProtocolDetectTimeout = value
305 } else {
306 log.Warnf("unrecognised value used on pod annotation %s: %s", k8s.ProxyDisableOutboundProtocolDetectTimeout, err.Error())
307 }
308 }
309
310 if override, ok := annotations[k8s.ProxyDisableInboundProtocolDetectTimeout]; ok {
311 value, err := strconv.ParseBool(override)
312 if err == nil {
313 values.Proxy.DisableInboundProtocolDetectTimeout = value
314 } else {
315 log.Warnf("unrecognised value used on pod annotation %s: %s", k8s.ProxyDisableInboundProtocolDetectTimeout, err.Error())
316 }
317 }
318
319 if override, ok := annotations[k8s.ProxyShutdownGracePeriodAnnotation]; ok {
320 duration, err := time.ParseDuration(override)
321 if err != nil {
322 log.Warnf("unrecognized proxy-shutdown-grace-period duration value found on pod annotation: %s", err.Error())
323 } else {
324 values.Proxy.ShutdownGracePeriod = fmt.Sprintf("%dms", int(duration.Seconds()*1000))
325 }
326 }
327
328 if override, ok := annotations[k8s.ProxyEnableGatewayAnnotation]; ok {
329 value, err := strconv.ParseBool(override)
330 if err == nil {
331 values.Proxy.IsGateway = value
332 }
333 }
334
335 if override, ok := annotations[k8s.ProxyWaitBeforeExitSecondsAnnotation]; ok {
336 waitBeforeExitSeconds, err := strconv.ParseUint(override, 10, 64)
337 if nil != err {
338 log.Warnf("unrecognized value used for the %s annotation, uint64 is expected: %s",
339 k8s.ProxyWaitBeforeExitSecondsAnnotation, override)
340 } else {
341 values.Proxy.WaitBeforeExitSeconds = waitBeforeExitSeconds
342 }
343 }
344
345 if override, ok := annotations[k8s.ProxyEnableNativeSidecarAnnotation]; ok {
346 value, err := strconv.ParseBool(override)
347 if err == nil {
348 values.Proxy.NativeSidecar = value
349 }
350 }
351
352 if override, ok := annotations[k8s.ProxyCPURequestAnnotation]; ok {
353 _, err := k8sResource.ParseQuantity(override)
354 if err != nil {
355 log.Warnf("%s (%s)", err, k8s.ProxyCPURequestAnnotation)
356 } else {
357 values.Proxy.Resources.CPU.Request = override
358 }
359 }
360
361 if override, ok := annotations[k8s.ProxyMemoryRequestAnnotation]; ok {
362 _, err := k8sResource.ParseQuantity(override)
363 if err != nil {
364 log.Warnf("%s (%s)", err, k8s.ProxyMemoryRequestAnnotation)
365 } else {
366 values.Proxy.Resources.Memory.Request = override
367 }
368 }
369
370 if override, ok := annotations[k8s.ProxyEphemeralStorageRequestAnnotation]; ok {
371 _, err := k8sResource.ParseQuantity(override)
372 if err != nil {
373 log.Warnf("%s (%s)", err, k8s.ProxyEphemeralStorageRequestAnnotation)
374 } else {
375 values.Proxy.Resources.EphemeralStorage.Request = override
376 }
377 }
378
379 if override, ok := annotations[k8s.ProxyCPULimitAnnotation]; ok {
380 q, err := k8sResource.ParseQuantity(override)
381 if err != nil {
382 log.Warnf("%s (%s)", err, k8s.ProxyCPULimitAnnotation)
383 } else {
384 values.Proxy.Resources.CPU.Limit = override
385
386 n, err := ToWholeCPUCores(q)
387 if err != nil {
388 log.Warnf("%s (%s)", err, k8s.ProxyCPULimitAnnotation)
389 }
390 values.Proxy.Cores = n
391 }
392 }
393
394 if override, ok := annotations[k8s.ProxyMemoryLimitAnnotation]; ok {
395 _, err := k8sResource.ParseQuantity(override)
396 if err != nil {
397 log.Warnf("%s (%s)", err, k8s.ProxyMemoryLimitAnnotation)
398 } else {
399 values.Proxy.Resources.Memory.Limit = override
400 }
401 }
402
403 if override, ok := annotations[k8s.ProxyEphemeralStorageLimitAnnotation]; ok {
404 _, err := k8sResource.ParseQuantity(override)
405 if err != nil {
406 log.Warnf("%s (%s)", err, k8s.ProxyEphemeralStorageLimitAnnotation)
407 } else {
408 values.Proxy.Resources.EphemeralStorage.Limit = override
409 }
410 }
411
412 if override, ok := annotations[k8s.ProxyUIDAnnotation]; ok {
413 v, err := strconv.ParseInt(override, 10, 64)
414 if err == nil {
415 values.Proxy.UID = v
416 }
417 }
418
419 if override, ok := annotations[k8s.ProxyGIDAnnotation]; ok {
420 v, err := strconv.ParseInt(override, 10, 64)
421 if err == nil {
422 values.Proxy.GID = v
423 }
424 }
425
426 if override, ok := annotations[k8s.ProxyEnableExternalProfilesAnnotation]; ok {
427 value, err := strconv.ParseBool(override)
428 if err == nil {
429 values.Proxy.EnableExternalProfiles = value
430 }
431 }
432
433 if override, ok := annotations[k8s.ProxyInitImageAnnotation]; ok {
434 values.ProxyInit.Image.Name = override
435 }
436
437 if override, ok := annotations[k8s.ProxyImagePullPolicyAnnotation]; ok {
438 values.ProxyInit.Image.PullPolicy = override
439 }
440
441 if override, ok := annotations[k8s.ProxyIgnoreInboundPortsAnnotation]; ok {
442 values.ProxyInit.IgnoreInboundPorts = override
443 }
444
445 if override, ok := annotations[k8s.ProxyIgnoreOutboundPortsAnnotation]; ok {
446 values.ProxyInit.IgnoreOutboundPorts = override
447 }
448
449 if override, ok := annotations[k8s.ProxyOpaquePortsAnnotation]; ok {
450 var opaquePorts strings.Builder
451 for _, pr := range util.ParseContainerOpaquePorts(override, namedPorts) {
452 if opaquePorts.Len() > 0 {
453 opaquePorts.WriteRune(',')
454 }
455 opaquePorts.WriteString(pr.ToString())
456 }
457
458 values.Proxy.OpaquePorts = opaquePorts.String()
459 }
460
461 if override, ok := annotations[k8s.DebugImageAnnotation]; ok {
462 values.DebugContainer.Image.Name = override
463 }
464
465 if override, ok := annotations[k8s.DebugImageVersionAnnotation]; ok {
466 values.DebugContainer.Image.Version = override
467 }
468
469 if override, ok := annotations[k8s.DebugImagePullPolicyAnnotation]; ok {
470 values.DebugContainer.Image.PullPolicy = override
471 }
472
473 if override, ok := annotations[k8s.ProxyAwait]; ok {
474 if override == k8s.Enabled || override == k8s.Disabled {
475 values.Proxy.Await = override == k8s.Enabled
476 } else {
477 log.Warnf("unrecognized value used for the %s annotation, valid values are: [%s, %s]", k8s.ProxyAwait, k8s.Enabled, k8s.Disabled)
478 }
479 }
480
481 if override, ok := annotations[k8s.ProxyDefaultInboundPolicyAnnotation]; ok {
482 if override != k8s.AllUnauthenticated && override != k8s.AllAuthenticated && override != k8s.ClusterUnauthenticated && override != k8s.ClusterAuthenticated && override != k8s.Deny {
483 log.Warnf("unrecognized value used for the %s annotation, valid values are: [%s, %s, %s, %s, %s]", k8s.ProxyDefaultInboundPolicyAnnotation, k8s.AllUnauthenticated, k8s.AllAuthenticated, k8s.ClusterUnauthenticated, k8s.ClusterAuthenticated, k8s.Deny)
484 } else {
485 values.Proxy.DefaultInboundPolicy = override
486 }
487 }
488
489 if override, ok := annotations[k8s.ProxySkipSubnetsAnnotation]; ok {
490 values.ProxyInit.SkipSubnets = override
491 }
492
493 if override, ok := annotations[k8s.ProxyAccessLogAnnotation]; ok {
494 values.Proxy.AccessLog = override
495 }
496 }
497
498
499 func NewResourceConfig(values *l5dcharts.Values, origin Origin, ns string) *ResourceConfig {
500 config := &ResourceConfig{
501 namespace: ns,
502 nsAnnotations: make(map[string]string),
503 values: values,
504 origin: origin,
505 }
506
507 config.workload.Meta = &metav1.ObjectMeta{}
508 config.pod.meta = &metav1.ObjectMeta{}
509
510 config.pod.labels = map[string]string{k8s.ControllerNSLabel: ns}
511 config.pod.annotations = map[string]string{}
512 return config
513 }
514
515
516 func (conf *ResourceConfig) WithKind(kind string) *ResourceConfig {
517 conf.workload.metaType = metav1.TypeMeta{Kind: kind}
518 return conf
519 }
520
521
522
523 func (conf *ResourceConfig) WithNsAnnotations(m map[string]string) *ResourceConfig {
524 conf.nsAnnotations = m
525 return conf
526 }
527
528
529
530 func (conf *ResourceConfig) WithOwnerRetriever(f OwnerRetrieverFunc) *ResourceConfig {
531 conf.ownerRetriever = f
532 return conf
533 }
534
535
536 func (conf *ResourceConfig) GetOwnerRef() *metav1.OwnerReference {
537 return conf.workload.ownerRef
538 }
539
540 func (conf *ResourceConfig) GetOverrideAnnotations() map[string]string {
541 return conf.pod.annotations
542 }
543
544 func (conf *ResourceConfig) GetNsAnnotations() map[string]string {
545 return conf.nsAnnotations
546 }
547
548 func (conf *ResourceConfig) GetWorkloadAnnotations() map[string]string {
549 if conf.IsPod() {
550 return conf.pod.meta.Annotations
551 }
552
553 return conf.workload.Meta.Annotations
554 }
555
556
557 func (conf *ResourceConfig) AppendPodAnnotations(annotations map[string]string) {
558 for annotation, value := range annotations {
559 conf.pod.annotations[annotation] = value
560 }
561 }
562
563
564 func (conf *ResourceConfig) AppendPodAnnotation(k, v string) {
565 conf.pod.annotations[k] = v
566 }
567
568
569 func (conf *ResourceConfig) YamlMarshalObj() ([]byte, error) {
570 j, err := getFilteredJSON(conf.workload.obj)
571 if err != nil {
572 return nil, err
573 }
574 return yaml.JSONToYAML(j)
575 }
576
577
578
579 func (conf *ResourceConfig) ParseMetaAndYAML(bytes []byte) (*Report, error) {
580 if err := conf.parse(bytes); err != nil {
581 return nil, err
582 }
583
584 return newReport(conf), nil
585 }
586
587
588
589 func (conf *ResourceConfig) FromObject(v runtime.Object) (*Report, error) {
590 if err := conf.populateMeta(v); err != nil {
591 return nil, err
592 }
593
594 return newReport(conf), nil
595 }
596
597
598 func (conf *ResourceConfig) GetValues() *l5dcharts.Values {
599 return conf.values
600 }
601
602 func (conf *ResourceConfig) getAnnotationOverrides() map[string]string {
603 overrides := map[string]string{}
604 for k, v := range conf.pod.meta.Annotations {
605 overrides[k] = v
606 }
607
608 if conf.origin != OriginCLI {
609 for k, v := range conf.pod.annotations {
610 overrides[k] = v
611 }
612 }
613 return overrides
614 }
615
616
617
618 func (conf *ResourceConfig) GetPodPatch(injectProxy bool) ([]byte, error) {
619 namedPorts := make(map[string]int32)
620 if conf.HasPodTemplate() {
621 namedPorts = util.GetNamedPorts(conf.pod.spec.Containers)
622 }
623
624 values, err := GetOverriddenValues(conf.values, conf.getAnnotationOverrides(), namedPorts)
625 values.Proxy.PodInboundPorts = getPodInboundPorts(conf.pod.spec)
626 if err != nil {
627 return nil, fmt.Errorf("could not generate Overridden Values: %w", err)
628 }
629
630 if values.ClusterNetworks != "" {
631 for _, network := range strings.Split(strings.Trim(values.ClusterNetworks, ","), ",") {
632 if _, _, err := net.ParseCIDR(network); err != nil {
633 return nil, fmt.Errorf("cannot parse destination get networks: %w", err)
634 }
635 }
636 }
637
638 patch := &podPatch{
639 Values: *values,
640 Annotations: map[string]string{},
641 Labels: map[string]string{},
642 }
643 switch strings.ToLower(conf.workload.metaType.Kind) {
644 case k8s.Pod:
645 case k8s.CronJob:
646 patch.PathPrefix = "/spec/jobTemplate/spec/template"
647 default:
648 patch.PathPrefix = "/spec/template"
649 }
650
651 if conf.pod.spec != nil {
652 conf.injectPodAnnotations(patch)
653 if injectProxy {
654 conf.injectObjectMeta(patch)
655 conf.injectPodSpec(patch)
656 } else {
657 patch.Proxy = nil
658 patch.ProxyInit = nil
659 }
660 }
661
662 rawValues, err := yaml.Marshal(patch)
663 if err != nil {
664 return nil, err
665 }
666
667 files := []*loader.BufferedFile{
668 {Name: chartutil.ChartfileName},
669 {Name: "requirements.yaml"},
670 {Name: "templates/patch.json"},
671 }
672
673 chart := &charts.Chart{
674 Name: "patch",
675 Dir: "patch",
676 Namespace: conf.namespace,
677 RawValues: rawValues,
678 Files: files,
679 Fs: static.Templates,
680 }
681 buf, err := chart.Render()
682 if err != nil {
683 return nil, err
684 }
685
686
687 res := rTrail.ReplaceAll(buf.Bytes(), []byte("}\n]"))
688
689 return res, nil
690 }
691
692
693
694
695
696 func GetConfigOverride(annotationKey string, workloadAnn map[string]string, nsAnn map[string]string) (string, bool) {
697 _, ok := workloadAnn[annotationKey]
698 if ok {
699 log.Debugf("using workload %s annotation value", annotationKey)
700 return "", false
701 }
702
703 annotation, ok := nsAnn[annotationKey]
704 if ok {
705 log.Debugf("using namespace %s annotation value", annotationKey)
706 return annotation, true
707 }
708 return "", false
709 }
710
711
712
713 func (conf *ResourceConfig) CreateOpaquePortsPatch() ([]byte, error) {
714 if conf.HasWorkloadAnnotation(k8s.ProxyOpaquePortsAnnotation) {
715
716
717 return nil, nil
718 }
719 workloadAnn := conf.workload.Meta.Annotations
720 if conf.IsPod() {
721 workloadAnn = conf.pod.meta.Annotations
722 }
723
724 opaquePorts, ok := GetConfigOverride(k8s.ProxyOpaquePortsAnnotation, workloadAnn, conf.nsAnnotations)
725 if ok {
726
727
728
729 return conf.CreateAnnotationPatch(opaquePorts)
730 }
731
732
733
734 defaultPorts := strings.Split(conf.GetValues().Proxy.OpaquePorts, ",")
735 var filteredPorts []string
736 if conf.IsPod() {
737
738
739 filteredPorts = conf.FilterPodOpaquePorts(defaultPorts)
740 } else if conf.IsService() {
741
742
743 service := conf.workload.obj.(*corev1.Service)
744 for _, p := range service.Spec.Ports {
745 port := strconv.Itoa(int(p.Port))
746 if p.TargetPort.Type == 0 && p.TargetPort.IntVal == 0 {
747
748
749
750
751
752 if util.ContainsString(port, defaultPorts) {
753 filteredPorts = append(filteredPorts, port)
754 }
755 } else if util.ContainsString(strconv.Itoa(int(p.TargetPort.IntVal)), defaultPorts) {
756
757
758 filteredPorts = append(filteredPorts, port)
759 }
760 }
761 }
762 if len(filteredPorts) == 0 {
763
764
765 return nil, nil
766 }
767 ports := strings.Join(filteredPorts, ",")
768 return conf.CreateAnnotationPatch(ports)
769 }
770
771
772
773 func (conf *ResourceConfig) FilterPodOpaquePorts(defaultPorts []string) []string {
774 var filteredPorts []string
775 for _, c := range conf.pod.spec.Containers {
776 for _, p := range c.Ports {
777 port := strconv.Itoa(int(p.ContainerPort))
778 if util.ContainsString(port, defaultPorts) {
779 filteredPorts = append(filteredPorts, port)
780 }
781 }
782 }
783 return filteredPorts
784 }
785
786
787
788 func (conf *ResourceConfig) HasWorkloadAnnotation(annotation string) bool {
789 if _, ok := conf.pod.meta.Annotations[annotation]; ok {
790 return true
791 }
792 if _, ok := conf.workload.Meta.Annotations[annotation]; ok {
793 return true
794 }
795 _, ok := conf.pod.annotations[annotation]
796 return ok
797 }
798
799
800
801 func (conf *ResourceConfig) CreateAnnotationPatch(opaquePorts string) ([]byte, error) {
802 addRootAnnotations := false
803 if conf.IsPod() {
804 addRootAnnotations = len(conf.pod.meta.Annotations) == 0
805 } else {
806 addRootAnnotations = len(conf.workload.Meta.Annotations) == 0
807 }
808
809 patch := &annotationPatch{
810 AddRootAnnotations: addRootAnnotations,
811 OpaquePorts: opaquePorts,
812 }
813 t, err := template.New("tpl").Parse(tpl)
814 if err != nil {
815 return nil, err
816 }
817 var patchJSON bytes.Buffer
818 if err = t.Execute(&patchJSON, patch); err != nil {
819 return nil, err
820 }
821 return patchJSON.Bytes(), nil
822 }
823
824
825 func (conf *ResourceConfig) getFreshWorkloadObj() runtime.Object {
826 switch strings.ToLower(conf.workload.metaType.Kind) {
827 case k8s.Deployment:
828 return &appsv1.Deployment{}
829 case k8s.ReplicationController:
830 return &corev1.ReplicationController{}
831 case k8s.ReplicaSet:
832 return &appsv1.ReplicaSet{}
833 case k8s.Job:
834 return &batchv1.Job{}
835 case k8s.DaemonSet:
836 return &appsv1.DaemonSet{}
837 case k8s.StatefulSet:
838 return &appsv1.StatefulSet{}
839 case k8s.Pod:
840 return &corev1.Pod{}
841 case k8s.Namespace:
842 return &corev1.Namespace{}
843 case k8s.CronJob:
844 return &batchv1.CronJob{}
845 case k8s.Service:
846 return &corev1.Service{}
847 }
848
849 return nil
850 }
851
852
853
854 func (conf *ResourceConfig) JSONToYAML(bytes []byte) ([]byte, error) {
855 obj := conf.getFreshWorkloadObj()
856 if err := json.Unmarshal(bytes, obj); err != nil {
857 return nil, err
858 }
859
860 j, err := getFilteredJSON(obj)
861 if err != nil {
862 return nil, err
863 }
864 return yaml.JSONToYAML(j)
865 }
866
867 func (conf *ResourceConfig) populateMeta(obj runtime.Object) error {
868 switch v := obj.(type) {
869 case *appsv1.Deployment:
870 conf.workload.obj = v
871 conf.workload.Meta = &v.ObjectMeta
872 conf.pod.labels[k8s.ProxyDeploymentLabel] = v.Name
873 conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
874 conf.complete(&v.Spec.Template)
875
876 case *corev1.ReplicationController:
877 conf.workload.obj = v
878 conf.workload.Meta = &v.ObjectMeta
879 conf.pod.labels[k8s.ProxyReplicationControllerLabel] = v.Name
880 conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
881 conf.complete(v.Spec.Template)
882
883 case *appsv1.ReplicaSet:
884 conf.workload.obj = v
885 conf.workload.Meta = &v.ObjectMeta
886 conf.pod.labels[k8s.ProxyReplicaSetLabel] = v.Name
887 conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
888 conf.complete(&v.Spec.Template)
889
890 case *batchv1.Job:
891 conf.workload.obj = v
892 conf.workload.Meta = &v.ObjectMeta
893 conf.pod.labels[k8s.ProxyJobLabel] = v.Name
894 conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
895 conf.complete(&v.Spec.Template)
896
897 case *appsv1.DaemonSet:
898 conf.workload.obj = v
899 conf.workload.Meta = &v.ObjectMeta
900 conf.pod.labels[k8s.ProxyDaemonSetLabel] = v.Name
901 conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
902 conf.complete(&v.Spec.Template)
903
904 case *appsv1.StatefulSet:
905 conf.workload.obj = v
906 conf.workload.Meta = &v.ObjectMeta
907 conf.pod.labels[k8s.ProxyStatefulSetLabel] = v.Name
908 conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
909 conf.complete(&v.Spec.Template)
910
911 case *corev1.Namespace:
912 conf.workload.obj = v
913 conf.workload.Meta = &v.ObjectMeta
914 if conf.workload.Meta.Annotations == nil {
915 conf.workload.Meta.Annotations = map[string]string{}
916 }
917
918 case *batchv1.CronJob:
919 conf.workload.obj = v
920 conf.workload.Meta = &v.ObjectMeta
921 conf.pod.labels[k8s.ProxyCronJobLabel] = v.Name
922 conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
923 conf.complete(&v.Spec.JobTemplate.Spec.Template)
924
925 case *corev1.Pod:
926 conf.workload.obj = v
927 conf.pod.spec = &v.Spec
928 conf.pod.meta = &v.ObjectMeta
929
930 if conf.ownerRetriever != nil {
931 kind, name, err := conf.ownerRetriever(v)
932 if err != nil {
933 return err
934 }
935 conf.workload.ownerRef = &metav1.OwnerReference{Kind: kind, Name: name}
936 switch kind {
937 case k8s.Deployment:
938 conf.pod.labels[k8s.ProxyDeploymentLabel] = name
939 case k8s.ReplicationController:
940 conf.pod.labels[k8s.ProxyReplicationControllerLabel] = name
941 case k8s.ReplicaSet:
942 conf.pod.labels[k8s.ProxyReplicaSetLabel] = name
943 case k8s.Job:
944 conf.pod.labels[k8s.ProxyJobLabel] = name
945 case k8s.DaemonSet:
946 conf.pod.labels[k8s.ProxyDaemonSetLabel] = name
947 case k8s.StatefulSet:
948 conf.pod.labels[k8s.ProxyStatefulSetLabel] = name
949 }
950 }
951 conf.pod.labels[k8s.WorkloadNamespaceLabel] = v.Namespace
952 if conf.pod.meta.Annotations == nil {
953 conf.pod.meta.Annotations = map[string]string{}
954 }
955
956 case *corev1.Service:
957 conf.workload.obj = v
958 conf.workload.Meta = &v.ObjectMeta
959 if conf.workload.Meta.Annotations == nil {
960 conf.workload.Meta.Annotations = map[string]string{}
961 }
962
963 default:
964 return fmt.Errorf("unsupported type %T", v)
965 }
966
967 return nil
968 }
969
970
971
972 func (conf *ResourceConfig) parse(bytes []byte) error {
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998 if err := yaml.Unmarshal(bytes, &conf.workload.metaType); err != nil {
999 return err
1000 }
1001 obj := conf.getFreshWorkloadObj()
1002
1003 switch v := obj.(type) {
1004 case *appsv1.Deployment,
1005 *corev1.ReplicationController,
1006 *appsv1.ReplicaSet,
1007 *batchv1.Job,
1008 *appsv1.DaemonSet,
1009 *appsv1.StatefulSet,
1010 *corev1.Namespace,
1011 *batchv1.CronJob,
1012 *corev1.Pod,
1013 *corev1.Service:
1014 if err := yaml.Unmarshal(bytes, v); err != nil {
1015 return err
1016 }
1017
1018 if err := conf.populateMeta(v); err != nil {
1019 return err
1020 }
1021
1022 default:
1023
1024
1025 if err := yaml.Unmarshal(bytes, &conf.workload); err != nil {
1026 return err
1027 }
1028 }
1029
1030 return nil
1031 }
1032
1033 func (conf *ResourceConfig) complete(template *corev1.PodTemplateSpec) {
1034 conf.pod.spec = &template.Spec
1035 conf.pod.meta = &template.ObjectMeta
1036 if conf.pod.meta.Annotations == nil {
1037 conf.pod.meta.Annotations = map[string]string{}
1038 }
1039 }
1040
1041
1042 func (conf *ResourceConfig) injectPodSpec(values *podPatch) {
1043 saVolumeMount := conf.serviceAccountVolumeMount()
1044
1045
1046
1047 if conf.pod.spec.Containers != nil && len(conf.pod.spec.Containers) > 0 {
1048 if sc := conf.pod.spec.Containers[0].SecurityContext; sc != nil && sc.Capabilities != nil {
1049 values.Proxy.Capabilities = &l5dcharts.Capabilities{
1050 Add: []string{},
1051 Drop: []string{},
1052 }
1053 for _, add := range sc.Capabilities.Add {
1054 values.Proxy.Capabilities.Add = append(values.Proxy.Capabilities.Add, string(add))
1055 }
1056 for _, drop := range sc.Capabilities.Drop {
1057 values.Proxy.Capabilities.Drop = append(values.Proxy.Capabilities.Drop, string(drop))
1058 }
1059 }
1060 }
1061
1062 if saVolumeMount != nil {
1063 values.Proxy.SAMountPath = &l5dcharts.VolumeMountPath{
1064 Name: saVolumeMount.Name,
1065 MountPath: saVolumeMount.MountPath,
1066 ReadOnly: saVolumeMount.ReadOnly,
1067 }
1068 }
1069
1070 if v := conf.pod.meta.Annotations[k8s.ProxyEnableDebugAnnotation]; v != "" {
1071 debug, err := strconv.ParseBool(v)
1072 if err != nil {
1073 log.Warnf("unrecognized value used for the %s annotation: %s", k8s.ProxyEnableDebugAnnotation, v)
1074 debug = false
1075 }
1076
1077 if debug {
1078 log.Infof("inject debug container")
1079 values.DebugContainer = &l5dcharts.DebugContainer{
1080 Image: &l5dcharts.Image{
1081 Name: conf.values.DebugContainer.Image.Name,
1082 Version: conf.values.DebugContainer.Image.Version,
1083 PullPolicy: conf.values.DebugContainer.Image.PullPolicy,
1084 },
1085 }
1086 }
1087 }
1088
1089 conf.injectProxyInit(values)
1090 values.AddRootVolumes = len(conf.pod.spec.Volumes) == 0
1091 }
1092
1093 func (conf *ResourceConfig) injectProxyInit(values *podPatch) {
1094
1095
1096 if values.Proxy.Capabilities != nil {
1097 values.ProxyInit.Capabilities = &l5dcharts.Capabilities{}
1098 values.ProxyInit.Capabilities.Add = values.Proxy.Capabilities.Add
1099 values.ProxyInit.Capabilities.Drop = []string{}
1100 for _, drop := range values.Proxy.Capabilities.Drop {
1101
1102 if drop == "NET_RAW" || drop == "NET_ADMIN" {
1103 continue
1104 }
1105 values.ProxyInit.Capabilities.Drop = append(values.ProxyInit.Capabilities.Drop, drop)
1106 }
1107 }
1108
1109 values.ProxyInit.SAMountPath = values.Proxy.SAMountPath
1110
1111 if v := conf.pod.meta.Annotations[k8s.CloseWaitTimeoutAnnotation]; v != "" {
1112 closeWait, err := time.ParseDuration(v)
1113 if err != nil {
1114 log.Warnf("invalid duration value used for the %s annotation: %s", k8s.CloseWaitTimeoutAnnotation, v)
1115 } else {
1116 values.ProxyInit.CloseWaitTimeoutSecs = int64(closeWait.Seconds())
1117 }
1118 }
1119
1120 values.AddRootInitContainers = len(conf.pod.spec.InitContainers) == 0
1121
1122 }
1123
1124 func (conf *ResourceConfig) serviceAccountVolumeMount() *corev1.VolumeMount {
1125
1126 if containers := conf.pod.spec.Containers; len(containers) > 0 {
1127 for _, vm := range containers[0].VolumeMounts {
1128 if vm.MountPath == k8s.MountPathServiceAccount {
1129 vm := vm
1130 return &vm
1131 }
1132 }
1133 }
1134 return nil
1135 }
1136
1137
1138
1139 func (conf *ResourceConfig) injectObjectMeta(values *podPatch) {
1140
1141
1142 if values.Proxy.Image.Version != "" {
1143 values.Annotations[k8s.ProxyVersionAnnotation] = values.Proxy.Image.Version
1144 } else {
1145 values.Annotations[k8s.ProxyVersionAnnotation] = values.LinkerdVersion
1146 }
1147
1148
1149 checksumBytes := sha256.Sum256([]byte(values.IdentityTrustAnchorsPEM))
1150 checksum := hex.EncodeToString(checksumBytes[:])
1151 values.Annotations[k8s.ProxyTrustRootSHA] = checksum
1152
1153 if len(conf.pod.labels) > 0 {
1154 values.AddRootLabels = len(conf.pod.meta.Labels) == 0
1155 for _, k := range sortedKeys(conf.pod.labels) {
1156 values.Labels[k] = conf.pod.labels[k]
1157 }
1158 }
1159 }
1160
1161 func (conf *ResourceConfig) injectPodAnnotations(values *podPatch) {
1162
1163
1164 emptyMeta := &metav1.ObjectMeta{Annotations: map[string]string{}}
1165
1166
1167 values.AddRootMetadata = reflect.DeepEqual(conf.pod.meta, emptyMeta)
1168 values.AddRootAnnotations = len(conf.pod.meta.Annotations) == 0
1169
1170 for _, k := range sortedKeys(conf.pod.annotations) {
1171 values.Annotations[k] = conf.pod.annotations[k]
1172
1173
1174
1175 conf.pod.meta.Annotations[k] = conf.pod.annotations[k]
1176 }
1177 }
1178
1179
1180 func (conf *ResourceConfig) GetOverriddenConfiguration() map[string]string {
1181 proxyOverrideConfig := map[string]string{}
1182 for _, annotation := range ProxyAnnotations {
1183 proxyOverrideConfig[annotation] = conf.pod.meta.Annotations[annotation]
1184 }
1185
1186 return proxyOverrideConfig
1187 }
1188
1189
1190 func (conf *ResourceConfig) IsControlPlaneComponent() bool {
1191 _, b := conf.pod.meta.Labels[k8s.ControllerComponentLabel]
1192 return b
1193 }
1194
1195 func sortedKeys(m map[string]string) []string {
1196 keys := []string{}
1197 for k := range m {
1198 keys = append(keys, k)
1199 }
1200
1201 sort.Strings(keys)
1202
1203 return keys
1204 }
1205
1206
1207 func (conf *ResourceConfig) IsNamespace() bool {
1208 return strings.ToLower(conf.workload.metaType.Kind) == k8s.Namespace
1209 }
1210
1211
1212 func (conf *ResourceConfig) IsService() bool {
1213 return strings.ToLower(conf.workload.metaType.Kind) == k8s.Service
1214 }
1215
1216
1217 func (conf *ResourceConfig) IsPod() bool {
1218 return strings.ToLower(conf.workload.metaType.Kind) == k8s.Pod
1219 }
1220
1221
1222 func (conf *ResourceConfig) HasPodTemplate() bool {
1223 return conf.pod.meta != nil && conf.pod.spec != nil
1224 }
1225
1226
1227 func (conf *ResourceConfig) AnnotateNamespace(annotations map[string]string) ([]byte, error) {
1228 ns, ok := conf.workload.obj.(*corev1.Namespace)
1229 if !ok {
1230 return nil, errors.New("can't inject namespace. Type assertion failed")
1231 }
1232 ns.Annotations[k8s.ProxyInjectAnnotation] = k8s.ProxyInjectEnabled
1233 if len(annotations) > 0 {
1234 for annotation, value := range annotations {
1235 ns.Annotations[annotation] = value
1236 }
1237 }
1238 j, err := getFilteredJSON(ns)
1239 if err != nil {
1240 return nil, err
1241 }
1242 return yaml.JSONToYAML(j)
1243 }
1244
1245
1246 func (conf *ResourceConfig) AnnotateService(annotations map[string]string) ([]byte, error) {
1247 service, ok := conf.workload.obj.(*corev1.Service)
1248 if !ok {
1249 return nil, errors.New("can't inject service. Type assertion failed")
1250 }
1251 if len(annotations) > 0 {
1252 for annotation, value := range annotations {
1253 service.Annotations[annotation] = value
1254 }
1255 }
1256 j, err := getFilteredJSON(service)
1257 if err != nil {
1258 return nil, err
1259 }
1260 return yaml.JSONToYAML(j)
1261 }
1262
1263
1264
1265
1266
1267 func getFilteredJSON(conf runtime.Object) ([]byte, error) {
1268 return jsonfilter.Marshal(&conf)
1269 }
1270
1271
1272 func ToWholeCPUCores(q k8sResource.Quantity) (int64, error) {
1273 q.RoundUp(0)
1274 if n, ok := q.AsInt64(); ok {
1275 return n, nil
1276 }
1277 return 0, fmt.Errorf("Could not parse cores: %s", q.String())
1278 }
1279
1280
1281
1282
1283
1284 func getPodInboundPorts(podSpec *corev1.PodSpec) string {
1285 ports := make(map[int32]struct{})
1286 if podSpec != nil {
1287 for _, container := range podSpec.Containers {
1288 for _, port := range container.Ports {
1289 ports[port.ContainerPort] = struct{}{}
1290 }
1291
1292 if readiness := container.ReadinessProbe; readiness != nil {
1293 if port, ok := getProbePort(readiness); ok {
1294 ports[port] = struct{}{}
1295 }
1296 }
1297
1298 if liveness := container.LivenessProbe; liveness != nil {
1299 if port, ok := getProbePort(liveness); ok {
1300 ports[port] = struct{}{}
1301 }
1302 }
1303 }
1304 }
1305
1306 portList := make([]string, 0, len(ports))
1307 for port := range ports {
1308 portList = append(portList, strconv.Itoa(int(port)))
1309 }
1310
1311
1312 sort.Strings(portList)
1313 return strings.Join(portList, ",")
1314 }
1315
1316
1317
1318
1319 func getProbePort(probe *corev1.Probe) (int32, bool) {
1320 if probe.HTTPGet != nil {
1321
1322
1323
1324 switch probe.HTTPGet.Port.Type {
1325 case intstr.Int:
1326 return probe.HTTPGet.Port.IntVal, true
1327 }
1328 }
1329
1330 return 0, false
1331 }
1332
View as plain text