1 package inject
2
3 import (
4 "errors"
5 "fmt"
6 "strings"
7
8 "github.com/linkerd/linkerd2/pkg/healthcheck"
9 "github.com/linkerd/linkerd2/pkg/k8s"
10 v1 "k8s.io/api/core/v1"
11 )
12
13 const (
14 hostNetworkEnabled = "host_network_enabled"
15 sidecarExists = "sidecar_already_exists"
16 unsupportedResource = "unsupported_resource"
17 injectEnableAnnotationAbsent = "injection_enable_annotation_absent"
18 injectDisableAnnotationPresent = "injection_disable_annotation_present"
19 annotationAtNamespace = "namespace"
20 annotationAtWorkload = "workload"
21 invalidInjectAnnotationWorkload = "invalid_inject_annotation_at_workload"
22 invalidInjectAnnotationNamespace = "invalid_inject_annotation_at_ns"
23 disabledAutomountServiceAccountToken = "disabled_automount_service_account_token_account"
24 udpPortsEnabled = "udp_ports_enabled"
25 )
26
27 var (
28
29 Reasons = map[string]string{
30 hostNetworkEnabled: "hostNetwork is enabled",
31 sidecarExists: "pod has a sidecar injected already",
32 unsupportedResource: "this resource kind is unsupported",
33 injectEnableAnnotationAbsent: fmt.Sprintf("neither the namespace nor the pod have the annotation \"%s:%s\"", k8s.ProxyInjectAnnotation, k8s.ProxyInjectEnabled),
34 injectDisableAnnotationPresent: fmt.Sprintf("pod has the annotation \"%s:%s\"", k8s.ProxyInjectAnnotation, k8s.ProxyInjectDisabled),
35 invalidInjectAnnotationWorkload: fmt.Sprintf("invalid value for annotation \"%s\" at workload", k8s.ProxyInjectAnnotation),
36 invalidInjectAnnotationNamespace: fmt.Sprintf("invalid value for annotation \"%s\" at namespace", k8s.ProxyInjectAnnotation),
37 disabledAutomountServiceAccountToken: "automountServiceAccountToken set to \"false\", with Values.identity.serviceAccountTokenProjection set to \"false\"",
38 udpPortsEnabled: "UDP port(s) configured on pod spec",
39 }
40 )
41
42
43
44 type Report struct {
45 Kind string
46 Name string
47 HostNetwork bool
48 Sidecar bool
49 UDP bool
50 UnsupportedResource bool
51 InjectDisabled bool
52 InjectDisabledReason string
53 InjectAnnotationAt string
54 Annotatable bool
55 Annotated bool
56 AutomountServiceAccountToken bool
57
58
59
60 Uninjected struct {
61
62 Proxy bool
63
64
65 ProxyInit bool
66 }
67 }
68
69
70
71 func newReport(conf *ResourceConfig) *Report {
72 var name string
73 if conf.IsPod() {
74 name = conf.pod.meta.Name
75 if name == "" {
76 name = conf.pod.meta.GenerateName
77 }
78 } else if m := conf.workload.Meta; m != nil {
79 name = m.Name
80 }
81
82 report := &Report{
83 Kind: strings.ToLower(conf.workload.metaType.Kind),
84 Name: name,
85 AutomountServiceAccountToken: true,
86 }
87
88 if conf.HasPodTemplate() {
89 report.InjectDisabled, report.InjectDisabledReason, report.InjectAnnotationAt = report.disabledByAnnotation(conf)
90 report.HostNetwork = conf.pod.spec.HostNetwork
91 report.Sidecar = healthcheck.HasExistingSidecars(conf.pod.spec)
92 report.UDP = checkUDPPorts(conf.pod.spec)
93 if conf.pod.spec.AutomountServiceAccountToken != nil &&
94 (conf.values != nil && !conf.values.Identity.ServiceAccountTokenProjection) {
95 report.AutomountServiceAccountToken = *conf.pod.spec.AutomountServiceAccountToken
96 }
97 if conf.origin == OriginWebhook {
98 if vm := conf.serviceAccountVolumeMount(); vm == nil {
99
100 if conf.values != nil && !conf.values.Identity.ServiceAccountTokenProjection {
101 report.AutomountServiceAccountToken = false
102 }
103 }
104 }
105 } else {
106 report.UnsupportedResource = true
107 }
108
109 if conf.HasPodTemplate() || conf.IsService() || conf.IsNamespace() {
110 report.Annotatable = true
111 }
112
113 return report
114 }
115
116
117 func (r *Report) ResName() string {
118 return fmt.Sprintf("%s/%s", r.Kind, r.Name)
119 }
120
121
122
123
124 func (r *Report) Injectable() (bool, []string) {
125 var reasons []string
126 if r.HostNetwork {
127 reasons = append(reasons, hostNetworkEnabled)
128 }
129 if r.Sidecar {
130 reasons = append(reasons, sidecarExists)
131 }
132 if r.UnsupportedResource {
133 reasons = append(reasons, unsupportedResource)
134 }
135 if r.InjectDisabled {
136 reasons = append(reasons, r.InjectDisabledReason)
137 }
138
139 if !r.AutomountServiceAccountToken {
140 reasons = append(reasons, disabledAutomountServiceAccountToken)
141 }
142
143 if len(reasons) > 0 {
144 return false, reasons
145 }
146 return true, nil
147 }
148
149
150 func (r *Report) IsAnnotatable() bool {
151 return r.Annotatable
152 }
153
154 func checkUDPPorts(t *v1.PodSpec) bool {
155
156 for _, container := range t.Containers {
157 for _, port := range container.Ports {
158 if port.Protocol == v1.ProtocolUDP {
159 return true
160 }
161 }
162 }
163 return false
164 }
165
166
167
168
169 func (r *Report) disabledByAnnotation(conf *ResourceConfig) (bool, string, string) {
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 podAnnotation := conf.pod.meta.Annotations[k8s.ProxyInjectAnnotation]
188 nsAnnotation := conf.nsAnnotations[k8s.ProxyInjectAnnotation]
189
190 if conf.origin == OriginCLI {
191 return podAnnotation == k8s.ProxyInjectDisabled, "", ""
192 }
193
194 if !isInjectAnnotationValid(nsAnnotation) {
195 return true, invalidInjectAnnotationNamespace, ""
196 }
197
198 if !isInjectAnnotationValid(podAnnotation) {
199 return true, invalidInjectAnnotationWorkload, ""
200 }
201
202 if nsAnnotation == k8s.ProxyInjectEnabled || nsAnnotation == k8s.ProxyInjectIngress {
203 if podAnnotation == k8s.ProxyInjectDisabled {
204 return true, injectDisableAnnotationPresent, annotationAtWorkload
205 }
206 return false, "", annotationAtNamespace
207 }
208
209 if podAnnotation != k8s.ProxyInjectEnabled && podAnnotation != k8s.ProxyInjectIngress {
210 return true, injectEnableAnnotationAbsent, ""
211 }
212
213 return false, "", annotationAtWorkload
214 }
215
216 func isInjectAnnotationValid(annotation string) bool {
217 if annotation != "" && !(annotation == k8s.ProxyInjectEnabled || annotation == k8s.ProxyInjectDisabled || annotation == k8s.ProxyInjectIngress) {
218 return false
219 }
220 return true
221 }
222
223
224
225
226
227 func (r *Report) ThrowInjectError() []error {
228
229 errs := []error{}
230
231 if !r.AutomountServiceAccountToken {
232 errs = append(errs, errors.New(Reasons[disabledAutomountServiceAccountToken]))
233 }
234
235 if r.HostNetwork {
236 errs = append(errs, errors.New(Reasons[hostNetworkEnabled]))
237 }
238
239 if r.Sidecar {
240 errs = append(errs, errors.New(Reasons[sidecarExists]))
241 }
242
243 if r.UDP {
244 errs = append(errs, errors.New(Reasons[udpPortsEnabled]))
245 }
246
247 return errs
248 }
249
View as plain text